/*-------------------------------------------------------------------------
 * drawElements Quality Program OpenGL ES 3.1 Module
 * -------------------------------------------------
 *
 * Copyright 2014 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 *//*!
 * \file
 * \brief Basic Layout Binding Tests.
 *//*--------------------------------------------------------------------*/

#include "es31fLayoutBindingTests.hpp"

#include "gluShaderProgram.hpp"
#include "gluPixelTransfer.hpp"
#include "gluTextureUtil.hpp"
#include "gluContextInfo.hpp"

#include "glwFunctions.hpp"
#include "glwEnums.hpp"

#include "tcuSurface.hpp"
#include "tcuTestLog.hpp"
#include "tcuTexture.hpp"
#include "tcuTextureUtil.hpp"
#include "tcuImageCompare.hpp"
#include "tcuStringTemplate.hpp"
#include "tcuRenderTarget.hpp"

#include "deString.h"
#include "deStringUtil.hpp"
#include "deRandom.hpp"

using tcu::TestLog;
using tcu::Vec2;
using tcu::Vec3;
using tcu::Vec4;

namespace deqp
{
namespace gles31
{
namespace Functional
{
namespace
{

enum TestType
{
	TESTTYPE_BINDING_SINGLE = 0,
	TESTTYPE_BINDING_MAX,
	TESTTYPE_BINDING_MULTIPLE,
	TESTTYPE_BINDING_ARRAY,
	TESTTYPE_BINDING_MAX_ARRAY,

	TESTTYPE_BINDING_LAST,
};

enum ShaderType
{
	SHADERTYPE_VERTEX = 0,
	SHADERTYPE_FRAGMENT,
	SHADERTYPE_TESS_CONTROL,
	SHADERTYPE_TESS_EVALUATION,
	SHADERTYPE_ALL,

	SHADERTYPE_LAST,
};

enum
{
	MAX_UNIFORM_MULTIPLE_INSTANCES	= 7,
	MAX_UNIFORM_ARRAY_SIZE			= 7,
};

std::string generateVertexShader (ShaderType shaderType, const std::string& shaderUniformDeclarations, const std::string& shaderBody)
{
	static const char* const s_simpleVertexShaderSource	=	"#version 310 es\n"
															"in highp vec4 a_position;\n"
															"void main (void)\n"
															"{\n"
															"	gl_Position = a_position;\n"
															"}\n";

	switch (shaderType)
	{
		case SHADERTYPE_VERTEX:
		case SHADERTYPE_ALL:
		{
			std::ostringstream vertexShaderSource;
			vertexShaderSource	<<	"#version 310 es\n"
								<<	"in highp vec4 a_position;\n"
								<<	"out highp vec4 v_color;\n"
								<<	"uniform highp int u_arrayNdx;\n\n"
								<<	shaderUniformDeclarations << "\n"
								<<	"void main (void)\n"
								<<	"{\n"
								<<	"	highp vec4 color;\n\n"
								<<	shaderBody << "\n"
								<<	"	v_color = color;\n"
								<<	"	gl_Position = a_position;\n"
								<<	"}\n";

			return vertexShaderSource.str();
		}

		case SHADERTYPE_FRAGMENT:
		case SHADERTYPE_TESS_CONTROL:
		case SHADERTYPE_TESS_EVALUATION:
			return s_simpleVertexShaderSource;

		default:
			DE_ASSERT(false);
			return "";
	}
}

std::string generateFragmentShader (ShaderType shaderType, const std::string& shaderUniformDeclarations, const std::string& shaderBody)
{
	static const char* const s_simpleFragmentShaderSource = "#version 310 es\n"
															"in highp vec4 v_color;\n"
															"layout(location = 0) out highp vec4 fragColor;\n"
															"void main (void)\n"
															"{\n"
															"	fragColor = v_color;\n"
															"}\n";

	switch (shaderType)
	{
		case SHADERTYPE_VERTEX:
		case SHADERTYPE_TESS_CONTROL:
		case SHADERTYPE_TESS_EVALUATION:
			return s_simpleFragmentShaderSource;

		case SHADERTYPE_FRAGMENT:
		{
			std::ostringstream fragmentShaderSource;
			fragmentShaderSource	<<	"#version 310 es\n"
									<<	"layout(location = 0) out highp vec4 fragColor;\n"
									<<	"uniform highp int u_arrayNdx;\n\n"
									<<	shaderUniformDeclarations << "\n"
									<<	"void main (void)\n"
									<<	"{\n"
									<<	"	highp vec4 color;\n\n"
									<<	shaderBody << "\n"
									<<	"	fragColor = color;\n"
									<<	"}\n";

			return fragmentShaderSource.str();
		}
		case SHADERTYPE_ALL:
		{
			std::ostringstream fragmentShaderSource;
			fragmentShaderSource	<<	"#version 310 es\n"
									<<	"in highp vec4 v_color;\n"
									<<	"layout(location = 0) out highp vec4 fragColor;\n"
									<<	"uniform highp int u_arrayNdx;\n\n"
									<<	shaderUniformDeclarations << "\n"
									<<	"void main (void)\n"
									<<	"{\n"
									<<	"	if (v_color.x > 2.0) discard;\n"
									<<	"	highp vec4 color;\n\n"
									<<	shaderBody << "\n"
									<<	"	fragColor = color;\n"
									<<	"}\n";

			return fragmentShaderSource.str();
		}

		default:
			DE_ASSERT(false);
			return "";
	}
}

std::string generateTessControlShader (ShaderType shaderType, const std::string& shaderUniformDeclarations, const std::string& shaderBody)
{
	static const char* const s_simpleTessContorlShaderSource =	"#version 310 es\n"
																"#extension GL_EXT_tessellation_shader : require\n"
																"layout (vertices=3) out;\n"
																"\n"
																"void main (void)\n"
																"{\n"
																"	gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;\n"
																"}\n";

	switch (shaderType)
	{
		case SHADERTYPE_VERTEX:
		case SHADERTYPE_FRAGMENT:
		case SHADERTYPE_TESS_EVALUATION:
			return s_simpleTessContorlShaderSource;

		case SHADERTYPE_TESS_CONTROL:
		case SHADERTYPE_ALL:
		{
			std::ostringstream tessControlShaderSource;
			tessControlShaderSource <<	"#version 310 es\n"
									<<	"#extension GL_EXT_tessellation_shader : require\n"
									<<	"layout (vertices=3) out;\n"
									<<	"\n"
									<<	"uniform highp int u_arrayNdx;\n\n"
									<<	shaderUniformDeclarations << "\n"
									<<	"void main (void)\n"
									<<	"{\n"
									<<	"	highp vec4 color;\n\n"
									<<	shaderBody << "\n"
									<<	"	gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;\n"
									<<	"}\n";

			return tessControlShaderSource.str();
		}

		default:
			DE_ASSERT(false);
			return "";
	}
}

std::string generateTessEvaluationShader (ShaderType shaderType, const std::string& shaderUniformDeclarations, const std::string& shaderBody)
{
	static const char* const s_simpleTessEvaluationShaderSource =	"#version 310 es\n"
																	"#extension GL_EXT_tessellation_shader : require\n"
																	"layout (triangles) in;\n"
																	"\n"
																	"void main (void)\n"
																	"{\n"
																	"	gl_Position = gl_TessCoord[0] * gl_in[0].gl_Position + gl_TessCoord[1] * gl_in[1].gl_Position + gl_TessCoord[2] * gl_in[2].gl_Position;\n"
																	"}\n";

	switch (shaderType)
	{
		case SHADERTYPE_VERTEX:
		case SHADERTYPE_FRAGMENT:
		case SHADERTYPE_TESS_CONTROL:
			return s_simpleTessEvaluationShaderSource;

		case SHADERTYPE_TESS_EVALUATION:
		case SHADERTYPE_ALL:
		{
			std::ostringstream tessEvaluationShaderSource;
			tessEvaluationShaderSource	<< "#version 310 es\n"
										<< "#extension GL_EXT_tessellation_shader : require\n"
										<< "layout (triangles) in;\n"
										<< "\n"
										<< "uniform highp int u_arrayNdx;\n\n"
										<< shaderUniformDeclarations << "\n"
										<< "out mediump vec4 v_color;\n"
										<< "void main (void)\n"
										<< "{\n"
										<< "	highp vec4 color;\n\n"
										<<	shaderBody << "\n"
										<< "	v_color = color;\n"
										<< "	gl_Position = gl_TessCoord[0] * gl_in[0].gl_Position + gl_TessCoord[1] * gl_in[1].gl_Position + gl_TessCoord[2] * gl_in[2].gl_Position;\n"
										<< "}\n";

			return tessEvaluationShaderSource.str();
		}

		default:
			DE_ASSERT(false);
			return "";
	}
}

std::string getUniformName (const std::string& name, int declNdx)
{
	return name + de::toString(declNdx);
}

std::string getUniformName (const std::string& name, int declNdx, int arrNdx)
{
	return name + de::toString(declNdx) + "[" + de::toString(arrNdx) + "]";
}

Vec4 getRandomColor (de::Random& rnd)
{
	const float r = rnd.getFloat(0.2f, 0.9f);
	const float g = rnd.getFloat(0.2f, 0.9f);
	const float b = rnd.getFloat(0.2f, 0.9f);
	return Vec4(r, g, b, 1.0f);
}

class LayoutBindingRenderCase : public TestCase
{
public:
	enum
	{
		MAX_TEST_RENDER_WIDTH	= 256,
		MAX_TEST_RENDER_HEIGHT	= 256,
		TEST_TEXTURE_SIZE	= 1,
	};

										LayoutBindingRenderCase			(Context&			context,
																		 const char*		name,
																		 const char*		desc,
																		 ShaderType			shaderType,
																		 TestType			testType,
																		 glw::GLenum		maxBindingPointEnum,
																		 glw::GLenum		maxVertexUnitsEnum,
																		 glw::GLenum		maxFragmentUnitsEnum,
																		 glw::GLenum		maxCombinedUnitsEnum,
																		 const std::string& uniformName);
	virtual								~LayoutBindingRenderCase		(void);

	virtual void						init							(void);
	virtual void						deinit							(void);

	int									getRenderWidth					(void) const { return de::min((int)MAX_TEST_RENDER_WIDTH, m_context.getRenderTarget().getWidth()); }
	int									getRenderHeight					(void) const { return de::min((int)MAX_TEST_RENDER_HEIGHT, m_context.getRenderTarget().getHeight()); }
protected:
	virtual glu::ShaderProgram*			generateShaders					(void) const = 0;

	void								initRenderState					(void);
	bool								drawAndVerifyResult				(const Vec4& expectedColor);
	void								setTestResult					(bool queryTestPassed, bool imageTestPassed);

	const glu::ShaderProgram*			m_program;
	const ShaderType					m_shaderType;
	const TestType						m_testType;
	const std::string					m_uniformName;

	const glw::GLenum					m_maxBindingPointEnum;
	const glw::GLenum					m_maxVertexUnitsEnum;
	const glw::GLenum					m_maxFragmentUnitsEnum;
	const glw::GLenum					m_maxCombinedUnitsEnum;

	glw::GLuint							m_vao;
	glw::GLuint							m_vertexBuffer;
	glw::GLuint							m_indexBuffer;
	glw::GLint							m_shaderProgramLoc;
	glw::GLint							m_shaderProgramPosLoc;
	glw::GLint							m_shaderProgramArrayNdxLoc;
	glw::GLint							m_numBindings;

	std::vector<glw::GLint>				m_bindings;

private:
	void								initBindingPoints				(int minBindingPoint, int numBindingPoints);
};

LayoutBindingRenderCase::LayoutBindingRenderCase (Context&				context,
												  const char*			name,
												  const char*			desc,
												  ShaderType			shaderType,
												  TestType				testType,
												  glw::GLenum			maxBindingPointEnum,
												  glw::GLenum			maxVertexUnitsEnum,
												  glw::GLenum			maxFragmentUnitsEnum,
												  glw::GLenum			maxCombinedUnitsEnum,
												  const std::string&	uniformName)
	: TestCase						(context, name, desc)
	, m_program						(DE_NULL)
	, m_shaderType					(shaderType)
	, m_testType					(testType)
	, m_uniformName					(uniformName)
	, m_maxBindingPointEnum			(maxBindingPointEnum)
	, m_maxVertexUnitsEnum			(maxVertexUnitsEnum)
	, m_maxFragmentUnitsEnum		(maxFragmentUnitsEnum)
	, m_maxCombinedUnitsEnum		(maxCombinedUnitsEnum)
	, m_vao							(0)
	, m_vertexBuffer				(0)
	, m_indexBuffer					(0)
	, m_shaderProgramLoc			(0)
	, m_shaderProgramPosLoc			(0)
	, m_shaderProgramArrayNdxLoc	(0)
	, m_numBindings					(0)
{
}

LayoutBindingRenderCase::~LayoutBindingRenderCase (void)
{
	deinit();
}

void LayoutBindingRenderCase::init (void)
{
	const glw::Functions& gl = m_context.getRenderContext().getFunctions();

	{
		de::Random				rnd					(deStringHash(getName()) ^ 0xff23a4);
		glw::GLint				numBindingPoints	= 0;	// Number of available binding points
		glw::GLint				maxVertexUnits		= 0;	// Available uniforms in the vertex shader
		glw::GLint				maxFragmentUnits	= 0;	// Available uniforms in the fragment shader
		glw::GLint				maxCombinedUnits	= 0;	// Available uniforms in all the shader stages combined
		glw::GLint				maxUnits			= 0;	// Maximum available uniforms for this test

		gl.getIntegerv(m_maxVertexUnitsEnum, &maxVertexUnits);
		gl.getIntegerv(m_maxFragmentUnitsEnum, &maxFragmentUnits);
		gl.getIntegerv(m_maxCombinedUnitsEnum, &maxCombinedUnits);
		gl.getIntegerv(m_maxBindingPointEnum, &numBindingPoints);
		GLU_EXPECT_NO_ERROR(gl.getError(), "Querying available uniform numbers failed");

		m_testCtx.getLog() << tcu::TestLog::Message << "Maximum units for uniform type in the vertex shader: " << maxVertexUnits << tcu::TestLog::EndMessage;
		m_testCtx.getLog() << tcu::TestLog::Message << "Maximum units for uniform type in the fragment shader: " << maxFragmentUnits << tcu::TestLog::EndMessage;
		m_testCtx.getLog() << tcu::TestLog::Message << "Maximum combined units for uniform type: " << maxCombinedUnits << tcu::TestLog::EndMessage;
		m_testCtx.getLog() << tcu::TestLog::Message << "Maximum binding point for uniform type: " << numBindingPoints-1 << tcu::TestLog::EndMessage;

		// Select maximum number of uniforms used for the test
		switch (m_shaderType)
		{
			case SHADERTYPE_VERTEX:
				maxUnits = maxVertexUnits;
				break;

			case SHADERTYPE_FRAGMENT:
				maxUnits = maxFragmentUnits;
				break;

			case SHADERTYPE_ALL:
				maxUnits = maxCombinedUnits/2;
				break;

			default:
				DE_ASSERT(false);
		}

		// Select the number of uniforms (= bindings) used for this test
		switch (m_testType)
		{
			case TESTTYPE_BINDING_SINGLE:
			case TESTTYPE_BINDING_MAX:
				m_numBindings = 1;
				break;

			case TESTTYPE_BINDING_MULTIPLE:
				if (maxUnits < 2)
					throw tcu::NotSupportedError("Not enough uniforms available for test");
				m_numBindings = rnd.getInt(2, deMin32(MAX_UNIFORM_MULTIPLE_INSTANCES, maxUnits));
				break;

			case TESTTYPE_BINDING_ARRAY:
			case TESTTYPE_BINDING_MAX_ARRAY:
				if (maxUnits < 2)
					throw tcu::NotSupportedError("Not enough uniforms available for test");
				m_numBindings = rnd.getInt(2, deMin32(MAX_UNIFORM_ARRAY_SIZE, maxUnits));
				break;

			default:
				DE_ASSERT(false);
		}

		// Check that we have enough uniforms in different shaders to perform the tests
		if ( ((m_shaderType == SHADERTYPE_VERTEX) || (m_shaderType == SHADERTYPE_ALL)) && (maxVertexUnits < m_numBindings) )
			throw tcu::NotSupportedError("Vertex shader: not enough uniforms available for test");
		if ( ((m_shaderType == SHADERTYPE_FRAGMENT) || (m_shaderType == SHADERTYPE_ALL)) && (maxFragmentUnits < m_numBindings) )
			throw tcu::NotSupportedError("Fragment shader: not enough uniforms available for test");
		if ( (m_shaderType == SHADERTYPE_ALL) && (maxCombinedUnits < m_numBindings*2) )
			throw tcu::NotSupportedError("Not enough uniforms available for test");

		// Check that we have enough binding points to perform the tests
		if (numBindingPoints < m_numBindings)
			throw tcu::NotSupportedError("Not enough binding points available for test");

		// Initialize the binding points i.e. populate the two binding point vectors
		initBindingPoints(0, numBindingPoints);
	}

	// Generate the shader program - note: this must be done after deciding the binding points
	DE_ASSERT(!m_program);
	m_testCtx.getLog() << tcu::TestLog::Message << "Creating test shaders" << tcu::TestLog::EndMessage;
	m_program = generateShaders();
	m_testCtx.getLog() << *m_program;

	if (!m_program->isOk())
		throw tcu::TestError("Shader compile failed");

	// Setup vertex and index buffers
	{
		// Get attribute and uniform locations
		const deUint32	program	= m_program->getProgram();

		m_shaderProgramPosLoc		= gl.getAttribLocation(program, "a_position");
		m_shaderProgramArrayNdxLoc	= gl.getUniformLocation(program, "u_arrayNdx");
		m_vertexBuffer				= 0;
		m_indexBuffer				= 0;

		// Setup buffers so that we render one quad covering the whole viewport
		const Vec3 vertices[] =
		{
			Vec3(-1.0f, -1.0f, +1.0f),
			Vec3(+1.0f, -1.0f, +1.0f),
			Vec3(+1.0f, +1.0f, +1.0f),
			Vec3(-1.0f, +1.0f, +1.0f),
		};

		const deUint16 indices[] =
		{
			0, 1, 2,
			0, 2, 3,
		};

		TCU_CHECK((m_shaderProgramPosLoc >= 0) && (m_shaderProgramArrayNdxLoc >= 0));

		// Generate and bind vao
		if (!glu::isContextTypeES(m_context.getRenderContext().getType()))
		{
			gl.genVertexArrays(1, &m_vao);
			gl.bindVertexArray(m_vao);
		}

		// Generate and bind index buffer
		gl.genBuffers(1, &m_indexBuffer);
		gl.bindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_indexBuffer);
		gl.bufferData(GL_ELEMENT_ARRAY_BUFFER, (DE_LENGTH_OF_ARRAY(indices)*(glw::GLsizeiptr)sizeof(indices[0])), &indices[0], GL_STATIC_DRAW);
		GLU_EXPECT_NO_ERROR(gl.getError(), "Index buffer setup failed");

		// Generate and bind vertex buffer
		gl.genBuffers(1, &m_vertexBuffer);
		gl.bindBuffer(GL_ARRAY_BUFFER, m_vertexBuffer);
		gl.bufferData(GL_ARRAY_BUFFER, (DE_LENGTH_OF_ARRAY(vertices)*(glw::GLsizeiptr)sizeof(vertices[0])), &vertices[0], GL_STATIC_DRAW);
		gl.enableVertexAttribArray(m_shaderProgramPosLoc);
		gl.vertexAttribPointer(m_shaderProgramPosLoc, 3, GL_FLOAT, GL_FALSE, 0, DE_NULL);
		GLU_EXPECT_NO_ERROR(gl.getError(), "Vertex buffer setup failed");
	}
}

void LayoutBindingRenderCase::deinit (void)
{
	if (m_program)
	{
		delete m_program;
		m_program = DE_NULL;
	}

	if (m_shaderProgramPosLoc)
		m_context.getRenderContext().getFunctions().disableVertexAttribArray(m_shaderProgramPosLoc);

	if (m_vao)
		m_context.getRenderContext().getFunctions().deleteVertexArrays(1, &m_vao);

	if (m_vertexBuffer)
	{
		m_context.getRenderContext().getFunctions().deleteBuffers(1, &m_vertexBuffer);
		m_context.getRenderContext().getFunctions().bindBuffer(GL_ARRAY_BUFFER, 0);
	}

	if (m_indexBuffer)
	{
		m_context.getRenderContext().getFunctions().deleteBuffers(1, &m_indexBuffer);
		m_context.getRenderContext().getFunctions().bindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
	}
}

void LayoutBindingRenderCase::initBindingPoints (int minBindingPoint, int numBindingPoints)
{
	de::Random rnd(deStringHash(getName()) ^ 0xff23a4);

	switch (m_testType)
	{
		case TESTTYPE_BINDING_SINGLE:
		{
			const int bpoint = rnd.getInt(minBindingPoint, numBindingPoints-1);
			m_bindings.push_back(bpoint);
			break;
		}

		case TESTTYPE_BINDING_MAX:
			m_bindings.push_back(numBindingPoints-1);
			break;

		case TESTTYPE_BINDING_MULTIPLE:
		{
			// Choose multiple unique binding points from the low and high end of available binding points
			std::vector<deUint32> lowBindingPoints;
			std::vector<deUint32> highBindingPoints;

			for (int bpoint = 0; bpoint < numBindingPoints/2; ++bpoint)
				lowBindingPoints.push_back(bpoint);
			for (int bpoint = numBindingPoints/2; bpoint < numBindingPoints; ++bpoint)
				highBindingPoints.push_back(bpoint);

			rnd.shuffle(lowBindingPoints.begin(), lowBindingPoints.end());
			rnd.shuffle(highBindingPoints.begin(), highBindingPoints.end());

			for (int ndx = 0; ndx < m_numBindings; ++ndx)
			{
				if (ndx%2 == 0)
				{
					const int bpoint = lowBindingPoints.back();
					lowBindingPoints.pop_back();
					m_bindings.push_back(bpoint);
				}
				else
				{
					const int bpoint = highBindingPoints.back();
					highBindingPoints.pop_back();
					m_bindings.push_back(bpoint);
				}

			}
			break;
		}

		case TESTTYPE_BINDING_ARRAY:
		{
			const glw::GLint binding = rnd.getInt(minBindingPoint, numBindingPoints-m_numBindings);
			for (int ndx = 0; ndx < m_numBindings; ++ndx)
				m_bindings.push_back(binding+ndx);
			break;
		}

		case TESTTYPE_BINDING_MAX_ARRAY:
		{
			const glw::GLint binding = numBindingPoints-m_numBindings;
			for (int ndx = 0; ndx < m_numBindings; ++ndx)
				m_bindings.push_back(binding+ndx);
			break;
		}

		default:
			DE_ASSERT(false);
	}
}

void LayoutBindingRenderCase::initRenderState (void)
{
	const glw::Functions& gl = m_context.getRenderContext().getFunctions();

	gl.useProgram(m_program->getProgram());
	gl.viewport(0, 0, getRenderWidth(), getRenderHeight());
	gl.clearColor(0.0f, 0.0f, 0.0f, 1.0f);
	GLU_EXPECT_NO_ERROR(gl.getError(), "Failed to set render state");
}

bool LayoutBindingRenderCase::drawAndVerifyResult (const Vec4& expectedColor)
{
	const glw::Functions&	gl					= m_context.getRenderContext().getFunctions();
	tcu::Surface			reference			(getRenderWidth(), getRenderHeight());

	// the point of these test is to check layout_binding. For this purpose, we can use quite
	// large thresholds.
	const tcu::RGBA			surfaceThreshold	= m_context.getRenderContext().getRenderTarget().getPixelFormat().getColorThreshold();
	const tcu::RGBA			compareThreshold	= tcu::RGBA(de::clamp(2 * surfaceThreshold.getRed(),   0, 255),
															de::clamp(2 * surfaceThreshold.getGreen(), 0, 255),
															de::clamp(2 * surfaceThreshold.getBlue(),  0, 255),
															de::clamp(2 * surfaceThreshold.getAlpha(), 0, 255));

	gl.clear(GL_COLOR_BUFFER_BIT);

	// Draw
	gl.drawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, DE_NULL);
	GLU_EXPECT_NO_ERROR(gl.getError(), "Drawing failed");

	// Verify
	tcu::Surface result(getRenderWidth(), getRenderHeight());
	m_testCtx.getLog() << TestLog::Message << "Reading pixels" << TestLog::EndMessage;
	glu::readPixels(m_context.getRenderContext(), 0, 0, result.getAccess());
	GLU_EXPECT_NO_ERROR(gl.getError(), "Read pixels failed");

	tcu::clear(reference.getAccess(), expectedColor);
	m_testCtx.getLog() << tcu::TestLog::Message << "Verifying output image, fragment output color is " << expectedColor << tcu::TestLog::EndMessage;

	return tcu::pixelThresholdCompare(m_testCtx.getLog(), "Render result", "Result verification", reference, result, compareThreshold, tcu::COMPARE_LOG_RESULT);
}

void LayoutBindingRenderCase::setTestResult (bool queryTestPassed, bool imageTestPassed)
{
	if (queryTestPassed && imageTestPassed)
		m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
	else if (!queryTestPassed && !imageTestPassed)
		m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "One or more binding point queries and image comparisons failed");
	else if (!queryTestPassed)
		m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "One or more binding point queries failed");
	else
		m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "One or more image comparisons failed");
}

class LayoutBindingNegativeCase : public TestCase
{
public:
	enum ErrorType
	{
		ERRORTYPE_OVER_MAX_UNITS = 0,
		ERRORTYPE_LESS_THAN_ZERO,
		ERRORTYPE_CONTRADICTORY,

		ERRORTYPE_LAST,
	};

										LayoutBindingNegativeCase		(Context&			context,
																		 const char*		name,
																		 const char*		desc,
																		 ShaderType			shaderType,
																		 TestType			testType,
																		 ErrorType			errorType,
																		 glw::GLenum		maxBindingPointEnum,
																		 glw::GLenum		maxVertexUnitsEnum,
																		 glw::GLenum		maxFragmentUnitsEnum,
																		 glw::GLenum		maxTessCtrlUnitsEnum,
																		 glw::GLenum		maxTessEvalUnitsEnum,
																		 glw::GLenum		maxCombinedUnitsEnum,
																		 const std::string& uniformName);
	virtual								~LayoutBindingNegativeCase		(void);

	virtual void						init							(void);
	virtual void						deinit							(void);
	virtual IterateResult				iterate							(void);

protected:
	virtual glu::ShaderProgram*			generateShaders					(void) const = 0;

	const glu::ShaderProgram*			m_program;
	const ShaderType					m_shaderType;
	const TestType						m_testType;
	const ErrorType						m_errorType;
	const glw::GLenum					m_maxBindingPointEnum;
	const glw::GLenum					m_maxVertexUnitsEnum;
	const glw::GLenum					m_maxFragmentUnitsEnum;
	const glw::GLenum					m_maxTessCtrlUnitsEnum;
	const glw::GLenum					m_maxTessEvalUnitsEnum;
	const glw::GLenum					m_maxCombinedUnitsEnum;
	const std::string					m_uniformName;
	glw::GLint							m_numBindings;
	std::vector<glw::GLint>				m_vertexShaderBinding;
	std::vector<glw::GLint>				m_fragmentShaderBinding;
	std::vector<glw::GLint>				m_tessCtrlShaderBinding;
	std::vector<glw::GLint>				m_tessEvalShaderBinding;
	bool								m_tessSupport;

private:
	void								initBindingPoints				(int minBindingPoint, int numBindingPoints);
};

LayoutBindingNegativeCase::LayoutBindingNegativeCase (Context&				context,
													  const char*			name,
													  const char*			desc,
													  ShaderType			shaderType,
													  TestType				testType,
													  ErrorType				errorType,
													  glw::GLenum			maxBindingPointEnum,
													  glw::GLenum			maxVertexUnitsEnum,
													  glw::GLenum			maxTessCtrlUnitsEnum,
													  glw::GLenum			maxTessEvalUnitsEnum,
													  glw::GLenum			maxFragmentUnitsEnum,
													  glw::GLenum			maxCombinedUnitsEnum,
													  const std::string&	uniformName)
	: TestCase					(context, name, desc)
	, m_program					(DE_NULL)
	, m_shaderType				(shaderType)
	, m_testType				(testType)
	, m_errorType				(errorType)
	, m_maxBindingPointEnum		(maxBindingPointEnum)
	, m_maxVertexUnitsEnum		(maxVertexUnitsEnum)
	, m_maxFragmentUnitsEnum	(maxFragmentUnitsEnum)
	, m_maxTessCtrlUnitsEnum	(maxTessCtrlUnitsEnum)
	, m_maxTessEvalUnitsEnum	(maxTessEvalUnitsEnum)
	, m_maxCombinedUnitsEnum	(maxCombinedUnitsEnum)
	, m_uniformName				(uniformName)
	, m_numBindings				(0)
	, m_tessSupport				(false)
{
}

LayoutBindingNegativeCase::~LayoutBindingNegativeCase (void)
{
	deinit();
}

void LayoutBindingNegativeCase::init (void)
{
	// Decide appropriate binding points for the vertex and fragment shaders
	const glw::Functions&	gl					= m_context.getRenderContext().getFunctions();
	de::Random				rnd					(deStringHash(getName()) ^ 0xff23a4);
	glw::GLint				numBindingPoints	= 0;	// Number of binding points
	glw::GLint				maxVertexUnits		= 0;	// Available uniforms in the vertex shader
	glw::GLint				maxFragmentUnits	= 0;	// Available uniforms in the fragment shader
	glw::GLint				maxCombinedUnits	= 0;	// Available uniforms in all the shader stages combined
	glw::GLint				maxTessCtrlUnits	= 0;	// Available uniforms in tessellation control shader
	glw::GLint				maxTessEvalUnits	= 0;	// Available uniforms in tessellation evaluation shader
	glw::GLint				maxUnits			= 0;	// Maximum available uniforms for this test

	m_tessSupport = m_context.getContextInfo().isExtensionSupported("GL_EXT_tessellation_shader")
					|| contextSupports(m_context.getRenderContext().getType(), glu::ApiType::es(3, 2))
					|| contextSupports(m_context.getRenderContext().getType(), glu::ApiType::core(4, 5));

	if (!m_tessSupport && (m_shaderType == SHADERTYPE_TESS_EVALUATION || m_shaderType == SHADERTYPE_TESS_CONTROL))
		TCU_THROW(NotSupportedError, "Tesselation shaders not supported");

	int numShaderStages = m_tessSupport ? 4 : 2;

	gl.getIntegerv(m_maxVertexUnitsEnum, &maxVertexUnits);
	gl.getIntegerv(m_maxFragmentUnitsEnum, &maxFragmentUnits);

	if (m_tessSupport)
	{
		gl.getIntegerv(m_maxTessCtrlUnitsEnum, &maxTessCtrlUnits);
		gl.getIntegerv(m_maxTessEvalUnitsEnum, &maxTessEvalUnits);
	}

	gl.getIntegerv(m_maxCombinedUnitsEnum, &maxCombinedUnits);
	gl.getIntegerv(m_maxBindingPointEnum, &numBindingPoints);
	GLU_EXPECT_NO_ERROR(gl.getError(), "Querying available uniform numbers failed");

	m_testCtx.getLog() << tcu::TestLog::Message << "Maximum units for uniform type in the vertex shader: " << maxVertexUnits << tcu::TestLog::EndMessage;
	m_testCtx.getLog() << tcu::TestLog::Message << "Maximum units for uniform type in the fragment shader: " << maxFragmentUnits << tcu::TestLog::EndMessage;

	if (m_tessSupport)
	{
		m_testCtx.getLog() << tcu::TestLog::Message << "Maximum units for uniform type in the tessellation control shader: " << maxTessCtrlUnits << tcu::TestLog::EndMessage;
		m_testCtx.getLog() << tcu::TestLog::Message << "Maximum units for uniform type in the tessellation evaluation shader: " << maxTessCtrlUnits << tcu::TestLog::EndMessage;
	}

	m_testCtx.getLog() << tcu::TestLog::Message << "Maximum combined units for uniform type: " << maxCombinedUnits << tcu::TestLog::EndMessage;
	m_testCtx.getLog() << tcu::TestLog::Message << "Maximum binding point for uniform type: " << numBindingPoints-1 << tcu::TestLog::EndMessage;

	// Select maximum number of uniforms used for the test
	switch (m_shaderType)
	{
		case SHADERTYPE_VERTEX:
			maxUnits = maxVertexUnits;
			break;

		case SHADERTYPE_FRAGMENT:
			maxUnits = maxFragmentUnits;
			break;

		case SHADERTYPE_ALL:
			maxUnits = de::min(de::min(de::min(maxVertexUnits, maxFragmentUnits), de::min(maxTessCtrlUnits, maxTessEvalUnits)), maxCombinedUnits/numShaderStages);
			break;

		case SHADERTYPE_TESS_CONTROL:
			maxUnits = maxTessCtrlUnits;
			break;

		case SHADERTYPE_TESS_EVALUATION:
			maxUnits = maxTessEvalUnits;
			break;

		default:
			DE_ASSERT(false);
	}

	// Select the number of uniforms (= bindings) used for this test
	switch (m_testType)
	{
		case TESTTYPE_BINDING_SINGLE:
		case TESTTYPE_BINDING_MAX:
			m_numBindings = 1;
			break;

		case TESTTYPE_BINDING_MULTIPLE:
		case TESTTYPE_BINDING_ARRAY:
		case TESTTYPE_BINDING_MAX_ARRAY:
			if (m_errorType == ERRORTYPE_CONTRADICTORY)
			{
				// leave room for contradictory case
				if (maxUnits < 3)
					TCU_THROW(NotSupportedError, "Not enough uniforms available for test");
				m_numBindings = rnd.getInt(2, deMin32(MAX_UNIFORM_ARRAY_SIZE, maxUnits-1));
			}
			else
			{
				if (maxUnits < 2)
					TCU_THROW(NotSupportedError, "Not enough uniforms available for test");
				m_numBindings = rnd.getInt(2, deMin32(MAX_UNIFORM_ARRAY_SIZE, maxUnits));
			}
			break;

		default:
			DE_ASSERT(false);
	}

	// Check that we have enough uniforms in different shaders to perform the tests
	if (((m_shaderType == SHADERTYPE_VERTEX) || (m_shaderType == SHADERTYPE_ALL)) && (maxVertexUnits < m_numBindings) )
		TCU_THROW(NotSupportedError, "Vertex shader: not enough uniforms available for test");

	if (((m_shaderType == SHADERTYPE_FRAGMENT) || (m_shaderType == SHADERTYPE_ALL)) && (maxFragmentUnits < m_numBindings) )
		TCU_THROW(NotSupportedError, "Fragment shader: not enough uniforms available for test");

	if (m_tessSupport && ((m_shaderType == SHADERTYPE_TESS_CONTROL) || (m_shaderType == SHADERTYPE_ALL)) && (maxTessCtrlUnits < m_numBindings) )
		TCU_THROW(NotSupportedError, "Tessellation control shader: not enough uniforms available for test");

	if (m_tessSupport && ((m_shaderType == SHADERTYPE_TESS_EVALUATION) || (m_shaderType == SHADERTYPE_ALL)) && (maxTessEvalUnits < m_numBindings) )
		TCU_THROW(NotSupportedError, "Tessellation evaluation shader: not enough uniforms available for test");

	if ((m_shaderType == SHADERTYPE_ALL) && (maxCombinedUnits < m_numBindings*numShaderStages) )
		TCU_THROW(NotSupportedError, "Not enough uniforms available for test");

	// Check that we have enough binding points to perform the tests
	if (numBindingPoints < m_numBindings)
		TCU_THROW(NotSupportedError, "Not enough binding points available for test");

	if (m_errorType == ERRORTYPE_CONTRADICTORY && numBindingPoints == m_numBindings)
		TCU_THROW(NotSupportedError, "Not enough binding points available for test");

	// Initialize the binding points i.e. populate the two binding point vectors
	initBindingPoints(0, numBindingPoints);

	// Generate the shader program - note: this must be done after deciding the binding points
	DE_ASSERT(!m_program);
	m_testCtx.getLog() << tcu::TestLog::Message << "Creating test shaders" << tcu::TestLog::EndMessage;
	m_program = generateShaders();
	m_testCtx.getLog() << *m_program;
}

void LayoutBindingNegativeCase::deinit (void)
{
	if (m_program)
	{
		delete m_program;
		m_program = DE_NULL;
	}
}

TestCase::IterateResult LayoutBindingNegativeCase::iterate (void)
{
	bool pass = false;
	std::string failMessage;

	switch (m_errorType)
	{
		case ERRORTYPE_CONTRADICTORY:		// Contradictory binding points should cause a link-time error
			if (!(m_program->getProgramInfo()).linkOk)
				pass = true;
			failMessage = "Test failed - expected a link-time error";
			break;

		case ERRORTYPE_LESS_THAN_ZERO:		// Out of bounds binding points should cause a compile-time error
		case ERRORTYPE_OVER_MAX_UNITS:
			if (m_tessSupport)
			{
				if (!(m_program->getShaderInfo(glu::SHADERTYPE_VERTEX)).compileOk
					|| !(m_program->getShaderInfo(glu::SHADERTYPE_FRAGMENT).compileOk)
					|| !(m_program->getShaderInfo(glu::SHADERTYPE_TESSELLATION_CONTROL).compileOk)
					|| !(m_program->getShaderInfo(glu::SHADERTYPE_TESSELLATION_EVALUATION)).compileOk)
					pass = true;
			}
			else
			{
				if (!(m_program->getShaderInfo(glu::SHADERTYPE_VERTEX)).compileOk
					|| !(m_program->getShaderInfo(glu::SHADERTYPE_FRAGMENT).compileOk))
					pass = true;
			}

			failMessage = "Test failed - expected a compile-time error";
			break;

		default:
			DE_ASSERT(false);
	}

	if (pass)
		m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
	else
		m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, failMessage.c_str());

	return STOP;
}

void LayoutBindingNegativeCase::initBindingPoints (int minBindingPoint, int numBindingPoints)
{
	de::Random rnd(deStringHash(getName()) ^ 0xff23a4);

	switch (m_errorType)
	{
		case ERRORTYPE_OVER_MAX_UNITS:	// Select a binding point that is 1 over the maximum
		{
			m_vertexShaderBinding.push_back(numBindingPoints+1-m_numBindings);
			m_fragmentShaderBinding.push_back(numBindingPoints+1-m_numBindings);
			m_tessCtrlShaderBinding.push_back(numBindingPoints+1-m_numBindings);
			m_tessEvalShaderBinding.push_back(numBindingPoints+1-m_numBindings);
			break;
		}

		case ERRORTYPE_LESS_THAN_ZERO:	// Select a random negative binding point
		{
			const glw::GLint binding = -rnd.getInt(1, m_numBindings);
			m_vertexShaderBinding.push_back(binding);
			m_fragmentShaderBinding.push_back(binding);
			m_tessCtrlShaderBinding.push_back(binding);
			m_tessEvalShaderBinding.push_back(binding);
			break;
		}

		case ERRORTYPE_CONTRADICTORY:	// Select two valid, but contradictory binding points
		{
			m_vertexShaderBinding.push_back(minBindingPoint);
			m_fragmentShaderBinding.push_back((minBindingPoint+1)%numBindingPoints);
			m_tessCtrlShaderBinding.push_back((minBindingPoint+2)%numBindingPoints);
			m_tessEvalShaderBinding.push_back((minBindingPoint+3)%numBindingPoints);

			DE_ASSERT(m_vertexShaderBinding.back()		!= m_fragmentShaderBinding.back());
			DE_ASSERT(m_fragmentShaderBinding.back()	!= m_tessEvalShaderBinding.back());
			DE_ASSERT(m_tessEvalShaderBinding.back()	!= m_tessCtrlShaderBinding.back());
			DE_ASSERT(m_tessCtrlShaderBinding.back()	!= m_vertexShaderBinding.back());
			DE_ASSERT(m_vertexShaderBinding.back()		!= m_tessEvalShaderBinding.back());
			DE_ASSERT(m_tessCtrlShaderBinding.back()	!= m_fragmentShaderBinding.back());
			break;
		}

		default:
			DE_ASSERT(false);
	}

	// In case we are testing with multiple uniforms populate the rest of the binding points
	for (int ndx = 1; ndx < m_numBindings; ++ndx)
	{
		m_vertexShaderBinding.push_back(m_vertexShaderBinding.front()+ndx);
		m_fragmentShaderBinding.push_back(m_fragmentShaderBinding.front()+ndx);
		m_tessCtrlShaderBinding.push_back(m_tessCtrlShaderBinding.front()+ndx);
		m_tessEvalShaderBinding.push_back(m_tessCtrlShaderBinding.front()+ndx);
	}
}

class SamplerBindingRenderCase : public LayoutBindingRenderCase
{
public:
									SamplerBindingRenderCase		(Context& context, const char* name, const char* desc, ShaderType shaderType, TestType testType, glw::GLenum samplerType, glw::GLenum textureType);
									~SamplerBindingRenderCase		(void);

	void							init							(void);
	void							deinit							(void);
	IterateResult					iterate							(void);

private:
	glu::ShaderProgram*				generateShaders					(void) const;
	glu::DataType					getSamplerTexCoordType			(void) const;
	void							initializeTexture				(glw::GLint bindingPoint, glw::GLint textureName, const Vec4& color) const;

	const glw::GLenum				m_samplerType;
	const glw::GLenum				m_textureType;

	std::vector<glw::GLuint>		m_textures;
	std::vector<Vec4>				m_textureColors;
};


SamplerBindingRenderCase::SamplerBindingRenderCase (Context&		context,
													const char*		name,
													const char*		desc,
													ShaderType		shaderType,
													TestType		testType,
													glw::GLenum		samplerType,
													glw::GLenum		textureType)
	: LayoutBindingRenderCase	(context, name, desc, shaderType, testType, GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS, GL_MAX_TEXTURE_IMAGE_UNITS, GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, "u_sampler")
	, m_samplerType				(samplerType)
	, m_textureType				(textureType)
{
}

SamplerBindingRenderCase::~SamplerBindingRenderCase (void)
{
	deinit();
}

void SamplerBindingRenderCase::init (void)
{
	LayoutBindingRenderCase::init();
	const glw::Functions&	gl		= m_context.getRenderContext().getFunctions();
	de::Random				rnd		(deStringHash(getName()) ^ 0xff23a4);


	// Initialize texture resources
	m_textures = std::vector<glw::GLuint>(m_numBindings,  0);

	// Texture colors
	for (int texNdx = 0; texNdx < (int)m_textures.size(); ++texNdx)
		m_textureColors.push_back(getRandomColor(rnd));

	// Textures
	gl.genTextures((glw::GLsizei)m_textures.size(), &m_textures[0]);

	for (int texNdx = 0; texNdx < (int)m_textures.size(); ++texNdx)
		initializeTexture(m_bindings[texNdx], m_textures[texNdx], m_textureColors[texNdx]);

	gl.activeTexture(GL_TEXTURE0);
}

void SamplerBindingRenderCase::deinit(void)
{
	LayoutBindingRenderCase::deinit();

	// Clean up texture data
	for (int i = 0; i < (int)m_textures.size(); ++i)
	{
		if (m_textures[i])
		{
			m_context.getRenderContext().getFunctions().deleteTextures(1, &m_textures[i]);
			m_context.getRenderContext().getFunctions().bindTexture(m_textureType, 0);
		}
	}
}

TestCase::IterateResult SamplerBindingRenderCase::iterate (void)
{
	const glw::Functions&	gl				= m_context.getRenderContext().getFunctions();
	const int				iterations		= m_numBindings;
	const bool				arrayInstance	= (m_testType == TESTTYPE_BINDING_ARRAY || m_testType == TESTTYPE_BINDING_MAX_ARRAY);
	bool					imageTestPassed	= true;
	bool					queryTestPassed	= true;

	// Set the viewport and enable the shader program
	initRenderState();

	for (int iterNdx = 0; iterNdx < iterations; ++iterNdx)
	{
		// Set the uniform value indicating the current array index
		gl.uniform1i(m_shaderProgramArrayNdxLoc, iterNdx);

		// Query binding point
		const std::string	name	= arrayInstance ? getUniformName(m_uniformName, 0, iterNdx) : getUniformName(m_uniformName, iterNdx);
		const glw::GLint	binding = m_bindings[iterNdx];
		glw::GLint			val		= -1;

		gl.getUniformiv(m_program->getProgram(), gl.getUniformLocation(m_program->getProgram(), name.c_str()), &val);
		m_testCtx.getLog() << tcu::TestLog::Message << "Querying binding point for " << name << ": " << val << " == " << binding << tcu::TestLog::EndMessage;
		GLU_EXPECT_NO_ERROR(gl.getError(), "Binding point query failed");

		// Draw and verify
		if (val != binding)
			queryTestPassed = false;
		if (!drawAndVerifyResult(m_textureColors[iterNdx]))
			imageTestPassed = false;
	}

	setTestResult(queryTestPassed, imageTestPassed);
	return STOP;
}

glu::ShaderProgram* SamplerBindingRenderCase::generateShaders (void) const
{
	std::ostringstream		shaderUniformDecl;
	std::ostringstream		shaderBody;

	const std::string		texCoordType	= glu::getDataTypeName(getSamplerTexCoordType());
	const std::string		samplerType		= glu::getDataTypeName(glu::getDataTypeFromGLType(m_samplerType));
	const bool				arrayInstance	= (m_testType == TESTTYPE_BINDING_ARRAY || m_testType == TESTTYPE_BINDING_MAX_ARRAY) ? true : false;
	const int				numDeclarations =  arrayInstance ? 1 : m_numBindings;

	// Generate the uniform declarations for the vertex and fragment shaders
	for (int declNdx = 0; declNdx < numDeclarations; ++declNdx)
	{
		shaderUniformDecl << "layout(binding = " << m_bindings[declNdx] << ") uniform highp " << samplerType << " "
			<< (arrayInstance ? getUniformName(m_uniformName, declNdx, m_numBindings) : getUniformName(m_uniformName, declNdx)) << ";\n";
	}

	// Generate the shader body for the vertex and fragment shaders
	for (int bindNdx = 0; bindNdx < m_numBindings; ++bindNdx)
	{
		shaderBody	<< "	" << (bindNdx == 0 ? "if" : "else if") << " (u_arrayNdx == " << de::toString(bindNdx) << ")\n"
					<< "	{\n"
					<< "		color = texture(" << (arrayInstance ? getUniformName(m_uniformName, 0, bindNdx) : getUniformName(m_uniformName, bindNdx)) << ", " << texCoordType << "(0.5));\n"
					<< "	}\n";
	}

	shaderBody	<< "	else\n"
				<< "	{\n"
				<< "		color = vec4(0.0, 0.0, 0.0, 1.0);\n"
				<< "	}\n";

	return new glu::ShaderProgram(m_context.getRenderContext(), glu::ProgramSources()
					<< glu::VertexSource(generateVertexShader(m_shaderType, shaderUniformDecl.str(), shaderBody.str()))
					<< glu::FragmentSource(generateFragmentShader(m_shaderType, shaderUniformDecl.str(), shaderBody.str())));
}

void SamplerBindingRenderCase::initializeTexture (glw::GLint bindingPoint, glw::GLint textureName, const Vec4& color) const
{
	const glw::Functions& gl = m_context.getRenderContext().getFunctions();

	gl.activeTexture(GL_TEXTURE0 + bindingPoint);
	gl.bindTexture(m_textureType, textureName);
	gl.texParameteri(m_textureType, GL_TEXTURE_MIN_FILTER, GL_LINEAR);

	switch (m_textureType)
	{
		case GL_TEXTURE_2D:
		{
			tcu::TextureLevel level(tcu::TextureFormat(tcu::TextureFormat::RGBA, tcu::TextureFormat::UNORM_INT8), TEST_TEXTURE_SIZE, TEST_TEXTURE_SIZE);
			tcu::clear(level.getAccess(), color);
			glu::texImage2D(m_context.getRenderContext(), m_textureType, 0, GL_RGBA8, level.getAccess());
			break;
		}

		case GL_TEXTURE_3D:
		{
			tcu::TextureLevel level(tcu::TextureFormat(tcu::TextureFormat::RGBA, tcu::TextureFormat::UNORM_INT8), TEST_TEXTURE_SIZE, TEST_TEXTURE_SIZE, TEST_TEXTURE_SIZE);
			tcu::clear(level.getAccess(), color);
			glu::texImage3D(m_context.getRenderContext(), m_textureType, 0, GL_RGBA8, level.getAccess());
			break;
		}

		default:
			DE_ASSERT(false);
	}

	GLU_EXPECT_NO_ERROR(gl.getError(), "Texture initialization failed");
}

glu::DataType SamplerBindingRenderCase::getSamplerTexCoordType (void) const
{
	switch (m_samplerType)
	{
		case GL_SAMPLER_2D:
			return glu::TYPE_FLOAT_VEC2;

		case GL_SAMPLER_3D:
			return glu::TYPE_FLOAT_VEC3;

		default:
			DE_ASSERT(false);
			return glu::TYPE_INVALID;
	}
}


class SamplerBindingNegativeCase : public LayoutBindingNegativeCase
{
public:
									SamplerBindingNegativeCase		(Context&		context,
																	 const char*	name,
																	 const char*	desc,
																	 ShaderType		shaderType,
																	 TestType		testType,
																	 ErrorType		errorType,
																	 glw::GLenum	samplerType);
									~SamplerBindingNegativeCase		(void);

private:
	glu::ShaderProgram*				generateShaders					(void) const;
	glu::DataType					getSamplerTexCoordType			(void) const;

	const glw::GLenum				m_samplerType;
};

SamplerBindingNegativeCase::SamplerBindingNegativeCase (Context&		context,
														const char*		name,
														const char*		desc,
														ShaderType		shaderType,
														TestType		testType,
														ErrorType		errorType,
														glw::GLenum		samplerType)
	: LayoutBindingNegativeCase		(context,
									 name,
									 desc,
									 shaderType,
									 testType,
									 errorType,
									 GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS,
									 GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS,
									 GL_MAX_TESS_CONTROL_TEXTURE_IMAGE_UNITS,
									 GL_MAX_TESS_EVALUATION_TEXTURE_IMAGE_UNITS,
									 GL_MAX_TEXTURE_IMAGE_UNITS,
									 GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS,
									 "u_sampler")
	, m_samplerType					(samplerType)
{
}

SamplerBindingNegativeCase::~SamplerBindingNegativeCase (void)
{
	LayoutBindingNegativeCase::deinit();
}

glu::ShaderProgram*	SamplerBindingNegativeCase::generateShaders	(void) const
{
	std::ostringstream		vertexUniformDecl;
	std::ostringstream		fragmentUniformDecl;
	std::ostringstream		tessCtrlUniformDecl;
	std::ostringstream		tessEvalUniformDecl;
	std::ostringstream		shaderBody;

	const std::string		texCoordType	= glu::getDataTypeName(getSamplerTexCoordType());
	const std::string		samplerType		= glu::getDataTypeName(glu::getDataTypeFromGLType(m_samplerType));
	const bool				arrayInstance	= (m_testType == TESTTYPE_BINDING_ARRAY || m_testType == TESTTYPE_BINDING_MAX_ARRAY);
	const int				numDeclarations = arrayInstance ? 1 : m_numBindings;

	// Generate the uniform declarations for the vertex and fragment shaders
	for (int declNdx = 0; declNdx < numDeclarations; ++declNdx)
	{
		vertexUniformDecl << "layout(binding = " << m_vertexShaderBinding[declNdx] << ") uniform highp " << samplerType
			<< " " << (arrayInstance ? getUniformName(m_uniformName, declNdx, m_numBindings) : getUniformName(m_uniformName, declNdx)) << ";\n";
		fragmentUniformDecl << "layout(binding = " << m_fragmentShaderBinding[declNdx] << ") uniform highp " << samplerType
			<< " " << (arrayInstance ? getUniformName(m_uniformName, declNdx, m_numBindings) : getUniformName(m_uniformName, declNdx)) << ";\n";
		tessCtrlUniformDecl << "layout(binding = " << m_tessCtrlShaderBinding[declNdx] << ") uniform highp " << samplerType
			<< " " << (arrayInstance ? getUniformName(m_uniformName, declNdx, m_numBindings) : getUniformName(m_uniformName, declNdx)) << ";\n";
		tessEvalUniformDecl << "layout(binding = " << m_tessEvalShaderBinding[declNdx] << ") uniform highp " << samplerType
			<< " " << (arrayInstance ? getUniformName(m_uniformName, declNdx, m_numBindings) : getUniformName(m_uniformName, declNdx)) << ";\n";
	}

	// Generate the shader body for the vertex and fragment shaders
	for (int bindNdx = 0; bindNdx < m_numBindings; ++bindNdx)
	{
		shaderBody	<< "	" << (bindNdx == 0 ? "if" : "else if") << " (u_arrayNdx == " << de::toString(bindNdx) << ")\n"
					<< "	{\n"
					<< "		color = texture(" << (arrayInstance ? getUniformName(m_uniformName, 0, bindNdx) : getUniformName(m_uniformName, bindNdx)) << ", " << texCoordType << "(0.5));\n"
					<< "	}\n";
	}

	shaderBody	<< "	else\n"
				<< "	{\n"
				<< "		color = vec4(0.0, 0.0, 0.0, 1.0);\n"
				<< "	}\n";

	glu::ProgramSources sources = glu::ProgramSources()
				<< glu::VertexSource(generateVertexShader(m_shaderType, vertexUniformDecl.str(), shaderBody.str()))
				<< glu::FragmentSource(generateFragmentShader(m_shaderType, fragmentUniformDecl.str(), shaderBody.str()));

	if (m_tessSupport)
		sources << glu::TessellationControlSource(generateTessControlShader(m_shaderType, tessCtrlUniformDecl.str(), shaderBody.str()))
				<< glu::TessellationEvaluationSource(generateTessEvaluationShader(m_shaderType, tessEvalUniformDecl.str(), shaderBody.str()));

	return new glu::ShaderProgram(m_context.getRenderContext(), sources);

}

glu::DataType SamplerBindingNegativeCase::getSamplerTexCoordType(void) const
{
	switch (m_samplerType)
	{
		case GL_SAMPLER_2D:
			return glu::TYPE_FLOAT_VEC2;

		case GL_SAMPLER_3D:
			return glu::TYPE_FLOAT_VEC3;

		default:
			DE_ASSERT(false);
			return glu::TYPE_INVALID;
	}
}

class ImageBindingRenderCase : public LayoutBindingRenderCase
{
public:
											ImageBindingRenderCase			(Context&		context,
																			 const char*	name,
																			 const char*	desc,
																			 ShaderType		shaderType,
																			 TestType		testType,
																			 glw::GLenum	imageType,
																			 glw::GLenum	textureType);
											~ImageBindingRenderCase			(void);

	void									init							(void);
	void									deinit							(void);
	IterateResult							iterate							(void);

private:
	glu::ShaderProgram*						generateShaders					(void) const;
	void									initializeImage					(glw::GLint imageBindingPoint, glw::GLint textureBindingPoint, glw::GLint textureName, const Vec4& color) const;
	glu::DataType							getImageTexCoordType			(void) const;

	const glw::GLenum						m_imageType;
	const glw::GLenum						m_textureType;

	std::vector<glw::GLuint>				m_textures;
	std::vector<Vec4>						m_textureColors;
};


ImageBindingRenderCase::ImageBindingRenderCase (Context&		context,
												const char*		name,
												const char*		desc,
												ShaderType		shaderType,
												TestType		testType,
												glw::GLenum		imageType,
												glw::GLenum		textureType)
	: LayoutBindingRenderCase		(context, name, desc, shaderType, testType, GL_MAX_IMAGE_UNITS, GL_MAX_VERTEX_IMAGE_UNIFORMS, GL_MAX_FRAGMENT_IMAGE_UNIFORMS, GL_MAX_COMBINED_IMAGE_UNIFORMS, "u_image")
	, m_imageType					(imageType)
	, m_textureType					(textureType)
{
}

ImageBindingRenderCase::~ImageBindingRenderCase (void)
{
	deinit();
}

void ImageBindingRenderCase::init (void)
{
	LayoutBindingRenderCase::init();

	const glw::Functions&	gl		= m_context.getRenderContext().getFunctions();
	de::Random				rnd		(deStringHash(getName()) ^ 0xff23a4);

	// Initialize image / texture resources
	m_textures = std::vector<glw::GLuint>(m_numBindings,  0);

	// Texture colors
	for (int texNdx = 0; texNdx < (int)m_textures.size(); ++texNdx)
		m_textureColors.push_back(getRandomColor(rnd));

	// Image textures
	gl.genTextures(m_numBindings, &m_textures[0]);

	for (int texNdx = 0; texNdx < (int)m_textures.size(); ++texNdx)
		initializeImage(m_bindings[texNdx], texNdx, m_textures[texNdx], m_textureColors[texNdx]);
}

void ImageBindingRenderCase::deinit (void)
{
	LayoutBindingRenderCase::deinit();

	// Clean up texture data
	for (int texNdx = 0; texNdx < (int)m_textures.size(); ++texNdx)
	{
		if (m_textures[texNdx])
		{
			m_context.getRenderContext().getFunctions().deleteTextures(1, &m_textures[texNdx]);
			m_context.getRenderContext().getFunctions().bindTexture(m_textureType, 0);
		}
	}
}

TestCase::IterateResult ImageBindingRenderCase::iterate	(void)
{
	const glw::Functions&	gl				= m_context.getRenderContext().getFunctions();
	const int				iterations		= m_numBindings;
	const bool				arrayInstance	= (m_testType == TESTTYPE_BINDING_ARRAY || m_testType == TESTTYPE_BINDING_MAX_ARRAY);
	bool					queryTestPassed	= true;
	bool					imageTestPassed = true;

	// Set the viewport and enable the shader program
	initRenderState();

	for (int iterNdx = 0; iterNdx < iterations; ++iterNdx)
	{
		// Set the uniform value indicating the current array index
		gl.uniform1i(m_shaderProgramArrayNdxLoc, iterNdx);

		const std::string	name	= (arrayInstance ? getUniformName(m_uniformName, 0, iterNdx) : getUniformName(m_uniformName, iterNdx));
		const glw::GLint	binding = m_bindings[iterNdx];
		glw::GLint			val		= -1;

		gl.getUniformiv(m_program->getProgram(), gl.getUniformLocation(m_program->getProgram(), name.c_str()), &val);
		m_testCtx.getLog() << tcu::TestLog::Message << "Querying binding point for " << name << ": " << val << " == " << binding << tcu::TestLog::EndMessage;
		GLU_EXPECT_NO_ERROR(gl.getError(), "Binding point query failed");

		// Draw and verify
		if (val != binding)
			queryTestPassed = false;
		if (!drawAndVerifyResult(m_textureColors[iterNdx]))
			imageTestPassed = false;
	}

	setTestResult(queryTestPassed, imageTestPassed);
	return STOP;
}

void ImageBindingRenderCase::initializeImage (glw::GLint imageBindingPoint, glw::GLint textureBindingPoint, glw::GLint textureName, const Vec4& color) const
{
	const glw::Functions& gl = m_context.getRenderContext().getFunctions();

	gl.activeTexture(GL_TEXTURE0 + textureBindingPoint);
	gl.bindTexture(m_textureType, textureName);
	gl.texParameteri(m_textureType, GL_TEXTURE_MIN_FILTER, GL_LINEAR);

	switch (m_textureType)
	{
		case GL_TEXTURE_2D:
		{
			tcu::TextureLevel level(tcu::TextureFormat(tcu::TextureFormat::RGBA, tcu::TextureFormat::UNORM_INT8), TEST_TEXTURE_SIZE, TEST_TEXTURE_SIZE);
			tcu::clear(level.getAccess(), color);
			gl.texStorage2D(m_textureType, 1, GL_RGBA8, TEST_TEXTURE_SIZE, TEST_TEXTURE_SIZE);
			gl.texSubImage2D(m_textureType, 0, 0, 0, TEST_TEXTURE_SIZE, TEST_TEXTURE_SIZE, GL_RGBA, GL_UNSIGNED_BYTE, level.getAccess().getDataPtr());
			break;
		}

		case GL_TEXTURE_3D:
		{
			tcu::TextureLevel level(tcu::TextureFormat(tcu::TextureFormat::RGBA, tcu::TextureFormat::UNORM_INT8), TEST_TEXTURE_SIZE, TEST_TEXTURE_SIZE, TEST_TEXTURE_SIZE);
			tcu::clear(level.getAccess(), color);
			gl.texStorage3D(m_textureType, 1, GL_RGBA8, TEST_TEXTURE_SIZE, TEST_TEXTURE_SIZE, TEST_TEXTURE_SIZE);
			gl.texSubImage3D(m_textureType, 0, 0, 0, 0, TEST_TEXTURE_SIZE, TEST_TEXTURE_SIZE, TEST_TEXTURE_SIZE, GL_RGBA, GL_UNSIGNED_BYTE, level.getAccess().getDataPtr());
			break;
		}

		default:
			DE_ASSERT(false);
	}

	gl.bindTexture(m_textureType, 0);
	gl.bindImageTexture(imageBindingPoint, textureName, 0, GL_TRUE, 0, GL_READ_ONLY, GL_RGBA8);
	GLU_EXPECT_NO_ERROR(gl.getError(), "Image initialization failed");
}

glu::ShaderProgram* ImageBindingRenderCase::generateShaders (void) const
{
	std::ostringstream		shaderUniformDecl;
	std::ostringstream		shaderBody;

	const std::string		texCoordType	= glu::getDataTypeName(getImageTexCoordType());
	const std::string		imageType		= glu::getDataTypeName(glu::getDataTypeFromGLType(m_imageType));
	const bool				arrayInstance	= (m_testType == TESTTYPE_BINDING_ARRAY || m_testType == TESTTYPE_BINDING_MAX_ARRAY) ? true : false;
	const int				numDeclarations = (arrayInstance ? 1 : m_numBindings);

	// Generate the uniform declarations for the vertex and fragment shaders
	for (int declNdx = 0; declNdx < numDeclarations; ++declNdx)
	{
		shaderUniformDecl << "layout(rgba8, binding = " << m_bindings[declNdx] << ") uniform readonly highp " << imageType
			<< " " << (arrayInstance ? getUniformName(m_uniformName, declNdx, m_numBindings) : getUniformName(m_uniformName, declNdx)) << ";\n";
	}

	// Generate the shader body for the vertex and fragment shaders
	for (int bindNdx = 0; bindNdx < m_numBindings; ++bindNdx)
	{
		shaderBody	<< "	" << (bindNdx == 0 ? "if" : "else if") << " (u_arrayNdx == " << de::toString(bindNdx) << ")\n"
					<< "	{\n"
					<< "		color = imageLoad(" << (arrayInstance ? getUniformName(m_uniformName, 0, bindNdx) : getUniformName(m_uniformName, bindNdx)) << ", " << texCoordType << "(0));\n"
					<< "	}\n";
	}

	shaderBody	<< "	else\n"
				<< "	{\n"
				<< "		color = vec4(0.0, 0.0, 0.0, 1.0);\n"
				<< "	}\n";

	return new glu::ShaderProgram(m_context.getRenderContext(), glu::ProgramSources()
					<< glu::VertexSource(generateVertexShader(m_shaderType, shaderUniformDecl.str(), shaderBody.str()))
					<< glu::FragmentSource(generateFragmentShader(m_shaderType, shaderUniformDecl.str(), shaderBody.str())));
}

glu::DataType ImageBindingRenderCase::getImageTexCoordType(void) const
{
	switch (m_imageType)
	{
		case GL_IMAGE_2D:
			return glu::TYPE_INT_VEC2;

		case GL_IMAGE_3D:
			return glu::TYPE_INT_VEC3;

		default:
			DE_ASSERT(false);
			return glu::TYPE_INVALID;
	}
}


class ImageBindingNegativeCase : public LayoutBindingNegativeCase
{
public:
											ImageBindingNegativeCase		(Context&		context,
																			 const char*	name,
																			 const char*	desc,
																			 ShaderType		shaderType,
																			 TestType		testType,
																			 ErrorType		errorType,
																			 glw::GLenum	imageType);
											~ImageBindingNegativeCase		(void);

private:
	glu::ShaderProgram*						generateShaders					(void) const;
	glu::DataType							getImageTexCoordType			(void) const;

	const glw::GLenum						m_imageType;
};

ImageBindingNegativeCase::ImageBindingNegativeCase (Context&		context,
													const char*		name,
													const char*		desc,
													ShaderType		shaderType,
													TestType		testType,
													ErrorType		errorType,
													glw::GLenum		imageType)
	: LayoutBindingNegativeCase		(context,
									 name,
									 desc,
									 shaderType,
									 testType,
									 errorType,
									 GL_MAX_IMAGE_UNITS,
									 GL_MAX_VERTEX_IMAGE_UNIFORMS,
									 GL_MAX_TESS_CONTROL_IMAGE_UNIFORMS,
									 GL_MAX_TESS_EVALUATION_IMAGE_UNIFORMS,
									 GL_MAX_FRAGMENT_IMAGE_UNIFORMS,
									 GL_MAX_COMBINED_IMAGE_UNIFORMS,
									 "u_image")
	, m_imageType					(imageType)
{
}

ImageBindingNegativeCase::~ImageBindingNegativeCase (void)
{
	deinit();
}

glu::ShaderProgram* ImageBindingNegativeCase::generateShaders (void) const
{
	std::ostringstream		vertexUniformDecl;
	std::ostringstream		fragmentUniformDecl;
	std::ostringstream		tessCtrlUniformDecl;
	std::ostringstream		tessEvalUniformDecl;
	std::ostringstream		shaderBody;

	const std::string		texCoordType	= glu::getDataTypeName(getImageTexCoordType());
	const std::string		imageType		= glu::getDataTypeName(glu::getDataTypeFromGLType(m_imageType));
	const bool				arrayInstance	= (m_testType == TESTTYPE_BINDING_ARRAY || m_testType == TESTTYPE_BINDING_MAX_ARRAY);
	const int				numDeclarations = (arrayInstance ? 1 : m_numBindings);

	// Generate the uniform declarations for the vertex and fragment shaders
	for (int declNdx = 0; declNdx < numDeclarations; ++declNdx)
	{
		vertexUniformDecl << "layout(rgba8, binding = " << m_vertexShaderBinding[declNdx] << ") uniform readonly highp " << imageType
			<< " " << (arrayInstance ? getUniformName(m_uniformName, declNdx, m_numBindings) : getUniformName(m_uniformName, declNdx)) << ";\n";
		fragmentUniformDecl << "layout(rgba8, binding = " << m_fragmentShaderBinding[declNdx] << ") uniform readonly highp " << imageType
			<< " " << (arrayInstance ? getUniformName(m_uniformName, declNdx, m_numBindings) : getUniformName(m_uniformName, declNdx)) << ";\n";
		tessCtrlUniformDecl << "layout(rgba8, binding = " << m_tessCtrlShaderBinding[declNdx] << ") uniform readonly highp " << imageType
			<< " " << (arrayInstance ? getUniformName(m_uniformName, declNdx, m_numBindings) : getUniformName(m_uniformName, declNdx)) << ";\n";
		tessEvalUniformDecl << "layout(rgba8, binding = " << m_tessEvalShaderBinding[declNdx] << ") uniform readonly highp " << imageType
			<< " " << (arrayInstance ? getUniformName(m_uniformName, declNdx, m_numBindings) : getUniformName(m_uniformName, declNdx)) << ";\n";
	}

	// Generate the shader body for the vertex and fragment shaders
	for (int bindNdx = 0; bindNdx < m_numBindings; ++bindNdx)
	{
		shaderBody	<< "	" << (bindNdx == 0 ? "if" : "else if") << " (u_arrayNdx == " << de::toString(bindNdx) << ")\n"
					<< "	{\n"
					<< "		color = imageLoad(" << (arrayInstance ? getUniformName(m_uniformName, 0, bindNdx) : getUniformName(m_uniformName, bindNdx)) << ", " << texCoordType << "(0));\n"
					<< "	}\n";
	}

	shaderBody	<< "	else\n"
				<< "	{\n"
				<< "		color = vec4(0.0, 0.0, 0.0, 1.0);\n"
				<< "	}\n";

	glu::ProgramSources sources = glu::ProgramSources()
				<< glu::VertexSource(generateVertexShader(m_shaderType, vertexUniformDecl.str(), shaderBody.str()))
				<< glu::FragmentSource(generateFragmentShader(m_shaderType, fragmentUniformDecl.str(), shaderBody.str()));

	if (m_tessSupport)
		sources << glu::TessellationControlSource(generateTessControlShader(m_shaderType, tessCtrlUniformDecl.str(), shaderBody.str()))
				<< glu::TessellationEvaluationSource(generateTessEvaluationShader(m_shaderType, tessEvalUniformDecl.str(), shaderBody.str()));

	return new glu::ShaderProgram(m_context.getRenderContext(), sources);
}

glu::DataType ImageBindingNegativeCase::getImageTexCoordType(void) const
{
	switch (m_imageType)
	{
		case GL_IMAGE_2D:
			return glu::TYPE_INT_VEC2;

		case GL_IMAGE_3D:
			return glu::TYPE_INT_VEC3;

		default:
			DE_ASSERT(false);
			return glu::TYPE_INVALID;
	}
}


class UBOBindingRenderCase : public LayoutBindingRenderCase
{
public:
											UBOBindingRenderCase		(Context&		context,
																		 const char*	name,
																		 const char*	desc,
																		 ShaderType		shaderType,
																		 TestType		testType);
											~UBOBindingRenderCase		(void);

	void									init						(void);
	void									deinit						(void);
	IterateResult							iterate						(void);

private:
	glu::ShaderProgram*						generateShaders				(void) const;

	std::vector<deUint32>					m_buffers;
	std::vector<Vec4>						m_expectedColors;
};

UBOBindingRenderCase::UBOBindingRenderCase (Context&		context,
											const char*		name,
											const char*		desc,
											ShaderType		shaderType,
											TestType		testType)
	: LayoutBindingRenderCase (context, name, desc, shaderType, testType, GL_MAX_UNIFORM_BUFFER_BINDINGS, GL_MAX_VERTEX_UNIFORM_BLOCKS, GL_MAX_FRAGMENT_UNIFORM_BLOCKS, GL_MAX_COMBINED_UNIFORM_BLOCKS, "ColorBlock")
{
}

UBOBindingRenderCase::~UBOBindingRenderCase (void)
{
	deinit();
}

void UBOBindingRenderCase::init (void)
{
	LayoutBindingRenderCase::init();

	const glw::Functions&	gl		= m_context.getRenderContext().getFunctions();
	de::Random				rnd		(deStringHash(getName()) ^ 0xff23a4);

	// Initialize UBOs and related data
	m_buffers = std::vector<glw::GLuint>(m_numBindings,  0);
	gl.genBuffers((glw::GLsizei)m_buffers.size(), &m_buffers[0]);

	for (int bufNdx = 0; bufNdx < (int)m_buffers.size(); ++bufNdx)
	{
			m_expectedColors.push_back(getRandomColor(rnd));
			m_expectedColors.push_back(getRandomColor(rnd));
	}

	for (int bufNdx = 0; bufNdx < (int)m_buffers.size(); ++bufNdx)
	{
		gl.bindBuffer(GL_UNIFORM_BUFFER, m_buffers[bufNdx]);
		gl.bufferData(GL_UNIFORM_BUFFER, 2*sizeof(Vec4), &(m_expectedColors[2*bufNdx]), GL_STATIC_DRAW);
		gl.bindBufferBase(GL_UNIFORM_BUFFER, m_bindings[bufNdx], m_buffers[bufNdx]);
	}

	GLU_EXPECT_NO_ERROR(gl.getError(), "UBO setup failed");
}

void UBOBindingRenderCase::deinit (void)
{
	LayoutBindingRenderCase::deinit();

	// Clean up UBO data
	for (int bufNdx = 0; bufNdx < (int)m_buffers.size(); ++bufNdx)
	{
		if (m_buffers[bufNdx])
		{
			m_context.getRenderContext().getFunctions().deleteBuffers(1, &m_buffers[bufNdx]);
			m_context.getRenderContext().getFunctions().bindBuffer(GL_UNIFORM_BUFFER, 0);
		}
	}
}

TestCase::IterateResult UBOBindingRenderCase::iterate (void)
{
	const glw::Functions&	gl				= m_context.getRenderContext().getFunctions();
	const int				iterations		= m_numBindings;
	const glw::GLenum		prop			= GL_BUFFER_BINDING;
	const bool				arrayInstance	= (m_testType == TESTTYPE_BINDING_ARRAY || m_testType == TESTTYPE_BINDING_MAX_ARRAY);
	bool					queryTestPassed	= true;
	bool					imageTestPassed = true;

	// Set the viewport and enable the shader program
	initRenderState();

	for (int iterNdx = 0; iterNdx < iterations; ++iterNdx)
	{
		// Query binding point
		const std::string	name	= (arrayInstance ? getUniformName(m_uniformName, 0, iterNdx) : getUniformName(m_uniformName, iterNdx));
		const glw::GLint	binding = m_bindings[iterNdx];
		glw::GLint			val		= -1;

		gl.getProgramResourceiv(m_program->getProgram(), GL_UNIFORM_BLOCK, gl.getProgramResourceIndex(m_program->getProgram(), GL_UNIFORM_BLOCK, name.c_str() ), 1, &prop, 1, DE_NULL, &val);
		m_testCtx.getLog() << tcu::TestLog::Message << "Querying binding point for " << name << ": " << val << " == " << binding << tcu::TestLog::EndMessage;
		GLU_EXPECT_NO_ERROR(gl.getError(), "Binding point query failed");

		if (val != binding)
			queryTestPassed = false;

		// Draw twice to render both colors within the UBO
		for (int drawCycle = 0; drawCycle < 2; ++drawCycle)
		{
			// Set the uniform indicating the array index to be used and set the expected color
			const int arrayNdx = iterNdx*2 + drawCycle;
			gl.uniform1i(m_shaderProgramArrayNdxLoc, arrayNdx);

			if (!drawAndVerifyResult(m_expectedColors[arrayNdx]))
				imageTestPassed = false;
		}
	}

	setTestResult(queryTestPassed, imageTestPassed);
	return STOP;
}

glu::ShaderProgram* UBOBindingRenderCase::generateShaders (void) const
{
	std::ostringstream		shaderUniformDecl;
	std::ostringstream		shaderBody;
	const bool				arrayInstance	= (m_testType == TESTTYPE_BINDING_ARRAY || m_testType == TESTTYPE_BINDING_MAX_ARRAY);
	const int				numDeclarations = (arrayInstance ? 1 : m_numBindings);

	// Generate the uniform declarations for the vertex and fragment shaders
	for (int declNdx = 0; declNdx < numDeclarations; ++declNdx)
	{
		shaderUniformDecl << "layout(std140, binding = " << m_bindings[declNdx] << ") uniform "
			<< getUniformName(m_uniformName, declNdx) << "\n"
			<< "{\n"
			<< "	highp vec4 color1;\n"
			<< "	highp vec4 color2;\n"
			<< "} " << (arrayInstance ? getUniformName("colors", declNdx, m_numBindings) : getUniformName("colors", declNdx)) << ";\n";
	}

	// Generate the shader body for the vertex and fragment shaders
	for (int bindNdx = 0; bindNdx < m_numBindings*2; ++bindNdx)	// Multiply by two to cover cases for both colors for each UBO
	{
		const std::string uname = (arrayInstance ? getUniformName("colors", 0, bindNdx/2) : getUniformName("colors", bindNdx/2));
		shaderBody	<< "	" << (bindNdx == 0 ? "if" : "else if") << " (u_arrayNdx == " << de::toString(bindNdx) << ")\n"
					<< "	{\n"
					<< "		color = " << uname << (bindNdx%2 == 0 ? ".color1" : ".color2") << ";\n"
					<< "	}\n";
	}

	shaderBody	<< "	else\n"
				<< "	{\n"
				<< "		color = vec4(0.0, 0.0, 0.0, 1.0);\n"
				<< "	}\n";

	return new glu::ShaderProgram(m_context.getRenderContext(), glu::ProgramSources()
					<< glu::VertexSource(generateVertexShader(m_shaderType, shaderUniformDecl.str(), shaderBody.str()))
					<< glu::FragmentSource(generateFragmentShader(m_shaderType, shaderUniformDecl.str(), shaderBody.str())));
}


class UBOBindingNegativeCase : public LayoutBindingNegativeCase
{
public:
											UBOBindingNegativeCase			(Context&		context,
																			 const char*	name,
																			 const char*	desc,
																			 ShaderType		shaderType,
																			 TestType		testType,
																			 ErrorType		errorType);
											~UBOBindingNegativeCase			(void);

private:
	glu::ShaderProgram*						generateShaders					(void) const;
};

UBOBindingNegativeCase::UBOBindingNegativeCase (Context&		context,
												const char*		name,
												const char*		desc,
												ShaderType		shaderType,
												TestType		testType,
												ErrorType		errorType)
	: LayoutBindingNegativeCase(context,
								name,
								desc,
								shaderType,
								testType,
								errorType,
								GL_MAX_UNIFORM_BUFFER_BINDINGS,
								GL_MAX_VERTEX_UNIFORM_BLOCKS,
								GL_MAX_TESS_CONTROL_UNIFORM_BLOCKS,
								GL_MAX_TESS_EVALUATION_UNIFORM_BLOCKS,
								GL_MAX_FRAGMENT_UNIFORM_BLOCKS,
								GL_MAX_COMBINED_UNIFORM_BLOCKS,
								"ColorBlock")
{
}

UBOBindingNegativeCase::~UBOBindingNegativeCase (void)
{
	deinit();
}

glu::ShaderProgram* UBOBindingNegativeCase::generateShaders (void) const
{
	std::ostringstream		vertexUniformDecl;
	std::ostringstream		fragmentUniformDecl;
	std::ostringstream		tessCtrlUniformDecl;
	std::ostringstream		tessEvalUniformDecl;
	std::ostringstream		shaderBody;
	const bool				arrayInstance	= (m_testType == TESTTYPE_BINDING_ARRAY || m_testType == TESTTYPE_BINDING_MAX_ARRAY);
	const int				numDeclarations = (arrayInstance ? 1 : m_numBindings);

	// Generate the uniform declarations for the vertex and fragment shaders
	for (int declNdx = 0; declNdx < numDeclarations; ++declNdx)
	{
		vertexUniformDecl << "layout(std140, binding = " << m_vertexShaderBinding[declNdx] << ") uniform "
			<< getUniformName(m_uniformName, declNdx) << "\n"
			<< "{\n"
			<< "	highp vec4 color1;\n"
			<< "	highp vec4 color2;\n"
			<< "} " << (arrayInstance ? getUniformName("colors", declNdx, m_numBindings) : getUniformName("colors", declNdx)) << ";\n";

		fragmentUniformDecl << "layout(std140, binding = " << m_fragmentShaderBinding[declNdx] << ") uniform "
			<< getUniformName(m_uniformName, declNdx) << "\n"
			<< "{\n"
			<< "	highp vec4 color1;\n"
			<< "	highp vec4 color2;\n"
			<< "} " << (arrayInstance ? getUniformName("colors", declNdx, m_numBindings) : getUniformName("colors", declNdx)) << ";\n";

		tessCtrlUniformDecl << "layout(std140, binding = " << m_tessCtrlShaderBinding[declNdx] << ") uniform "
			<< getUniformName(m_uniformName, declNdx) << "\n"
			<< "{\n"
			<< "	highp vec4 color1;\n"
			<< "	highp vec4 color2;\n"
			<< "} " << (arrayInstance ? getUniformName("colors", declNdx, m_numBindings) : getUniformName("colors", declNdx)) << ";\n";

		tessEvalUniformDecl << "layout(std140, binding = " << m_tessCtrlShaderBinding[declNdx] << ") uniform "
			<< getUniformName(m_uniformName, declNdx) << "\n"
			<< "{\n"
			<< "	highp vec4 color1;\n"
			<< "	highp vec4 color2;\n"
			<< "} " << (arrayInstance ? getUniformName("colors", declNdx, m_numBindings) : getUniformName("colors", declNdx)) << ";\n";
	}

	// Generate the shader body for the vertex and fragment shaders
	for (int bindNdx = 0; bindNdx < m_numBindings*2; ++bindNdx)	// Multiply by two to cover cases for both colors for each UBO
	{
		const std::string uname = (arrayInstance ? getUniformName("colors", 0, bindNdx/2) : getUniformName("colors", bindNdx/2));
		shaderBody	<< "	" << (bindNdx == 0 ? "if" : "else if") << " (u_arrayNdx == " << de::toString(bindNdx) << ")\n"
					<< "	{\n"
					<< "		color = " << uname << (bindNdx%2 == 0 ? ".color1" : ".color2") << ";\n"
					<< "	}\n";
	}

	shaderBody	<< "	else\n"
				<< "	{\n"
				<< "		color = vec4(0.0, 0.0, 0.0, 1.0);\n"
				<< "	}\n";

	glu::ProgramSources sources = glu::ProgramSources()
				<< glu::VertexSource(generateVertexShader(m_shaderType, vertexUniformDecl.str(), shaderBody.str()))
				<< glu::FragmentSource(generateFragmentShader(m_shaderType, fragmentUniformDecl.str(), shaderBody.str()));

	if (m_tessSupport)
		sources << glu::TessellationControlSource(generateTessControlShader(m_shaderType, tessCtrlUniformDecl.str(), shaderBody.str()))
				<< glu::TessellationEvaluationSource(generateTessEvaluationShader(m_shaderType, tessEvalUniformDecl.str(), shaderBody.str()));

	return new glu::ShaderProgram(m_context.getRenderContext(), sources);
}


class SSBOBindingRenderCase : public LayoutBindingRenderCase
{
public:
											SSBOBindingRenderCase		(Context&		context,
																		 const char*	name,
																		 const char*	desc,
																		 ShaderType		shaderType,
																		 TestType		testType);
											~SSBOBindingRenderCase		(void);

	void									init						(void);
	void									deinit						(void);
	IterateResult							iterate						(void);

private:
	glu::ShaderProgram*						generateShaders				(void) const;

	std::vector<glw::GLuint>				m_buffers;
	std::vector<Vec4>						m_expectedColors;
};

SSBOBindingRenderCase::SSBOBindingRenderCase (Context&		context,
											  const char*	name,
											  const char*	desc,
											  ShaderType	shaderType,
											  TestType		testType)
	: LayoutBindingRenderCase (context, name, desc, shaderType, testType, GL_MAX_SHADER_STORAGE_BUFFER_BINDINGS, GL_MAX_VERTEX_SHADER_STORAGE_BLOCKS, GL_MAX_FRAGMENT_SHADER_STORAGE_BLOCKS, GL_MAX_COMBINED_SHADER_STORAGE_BLOCKS, "ColorBuffer")
{
}

SSBOBindingRenderCase::~SSBOBindingRenderCase (void)
{
	deinit();
}

void SSBOBindingRenderCase::init (void)
{
	LayoutBindingRenderCase::init();

	const glw::Functions&	gl		= m_context.getRenderContext().getFunctions();
	de::Random				rnd		(deStringHash(getName()) ^ 0xff23a4);

	// Initialize SSBOs and related data
	m_buffers = std::vector<glw::GLuint>(m_numBindings, 0);
	gl.genBuffers((glw::GLsizei)m_buffers.size(), &m_buffers[0]);

	for (int bufNdx = 0; bufNdx < (int)m_buffers.size(); ++bufNdx)
	{
		m_expectedColors.push_back(getRandomColor(rnd));
		m_expectedColors.push_back(getRandomColor(rnd));
	}

	for (int bufNdx = 0; bufNdx < (int)m_buffers.size(); ++bufNdx)
	{
		gl.bindBuffer(GL_SHADER_STORAGE_BUFFER, m_buffers[bufNdx]);
		gl.bufferData(GL_SHADER_STORAGE_BUFFER, 2*sizeof(Vec4), &(m_expectedColors[2*bufNdx]), GL_STATIC_DRAW);
		gl.bindBufferBase(GL_SHADER_STORAGE_BUFFER, m_bindings[bufNdx], m_buffers[bufNdx]);
	}

	GLU_EXPECT_NO_ERROR(gl.getError(), "SSBO setup failed");
}

void SSBOBindingRenderCase::deinit (void)
{
	LayoutBindingRenderCase::deinit();

	// Clean up SSBO data
	for (int bufNdx = 0; bufNdx < (int)m_buffers.size(); ++bufNdx)
	{
		if (m_buffers[bufNdx])
		{
			m_context.getRenderContext().getFunctions().deleteBuffers(1, &m_buffers[bufNdx]);
			m_context.getRenderContext().getFunctions().bindBuffer(GL_SHADER_STORAGE_BUFFER, 0);
			m_buffers[bufNdx] = 0;
		}
	}
}

TestCase::IterateResult SSBOBindingRenderCase::iterate (void)
{
	const glw::Functions&	gl				= m_context.getRenderContext().getFunctions();
	const int				iterations		= m_numBindings;
	const glw::GLenum		prop			= GL_BUFFER_BINDING;
	const bool				arrayInstance	= (m_testType == TESTTYPE_BINDING_ARRAY || m_testType == TESTTYPE_BINDING_MAX_ARRAY);
	bool					queryTestPassed	= true;
	bool					imageTestPassed = true;

	initRenderState();

	for (int iterNdx = 0; iterNdx < iterations; ++iterNdx)
	{
		// Query binding point
		const std::string	name	= (arrayInstance ? getUniformName(m_uniformName, 0, iterNdx) : getUniformName(m_uniformName, iterNdx));
		const glw::GLint	binding = m_bindings[iterNdx];
		glw::GLint			val		= -1;

		gl.getProgramResourceiv(m_program->getProgram(), GL_SHADER_STORAGE_BLOCK, gl.getProgramResourceIndex(m_program->getProgram(), GL_SHADER_STORAGE_BLOCK, name.c_str() ), 1, &prop, 1, DE_NULL, &val);
		m_testCtx.getLog() << tcu::TestLog::Message << "Querying binding point for " << name << ": " << val << " == " << binding << tcu::TestLog::EndMessage;
		GLU_EXPECT_NO_ERROR(gl.getError(), "Binding point query failed");

		if (val != binding)
			queryTestPassed = false;

		// Draw twice to render both colors within the SSBO
		for (int drawCycle = 0; drawCycle < 2; ++drawCycle)
		{
			// Set the uniform indicating the array index to be used and set the expected color
			const int arrayNdx = iterNdx*2 + drawCycle;
			gl.uniform1i(m_shaderProgramArrayNdxLoc, arrayNdx);

			if (!drawAndVerifyResult(m_expectedColors[arrayNdx]))
				imageTestPassed = false;
		}
	}

	setTestResult(queryTestPassed, imageTestPassed);
	return STOP;
}

glu::ShaderProgram* SSBOBindingRenderCase::generateShaders (void) const
{
	std::ostringstream		shaderUniformDecl;
	std::ostringstream		shaderBody;
	const bool				arrayInstance	= (m_testType == TESTTYPE_BINDING_ARRAY || m_testType == TESTTYPE_BINDING_MAX_ARRAY);
	const int				numDeclarations = (arrayInstance ? 1 : m_numBindings);

	// Generate the uniform declarations for the vertex and fragment shaders
	for (int declNdx = 0; declNdx < numDeclarations; ++declNdx)
	{
		shaderUniformDecl << "layout(std140, binding = " << m_bindings[declNdx] << ") buffer "
			<< getUniformName(m_uniformName, declNdx) << "\n"
			<< "{\n"
			<< "	highp vec4 color1;\n"
			<< "	highp vec4 color2;\n"
			<< "} " << (arrayInstance ? getUniformName("colors", declNdx, m_numBindings) : getUniformName("colors", declNdx)) << ";\n";
	}

	// Generate the shader body for the vertex and fragment shaders
	for (int bindNdx = 0; bindNdx < m_numBindings*2; ++bindNdx)	// Multiply by two to cover cases for both colors for each UBO
	{
		const std::string uname = (arrayInstance ? getUniformName("colors", 0, bindNdx/2) : getUniformName("colors", bindNdx/2));
		shaderBody	<< "	" << (bindNdx == 0 ? "if" : "else if") << " (u_arrayNdx == " << de::toString(bindNdx) << ")\n"
					<< "	{\n"
					<< "		color = " << uname << (bindNdx%2 == 0 ? ".color1" : ".color2") << ";\n"
					<< "	}\n";
	}

	shaderBody	<< "	else\n"
				<< "	{\n"
				<< "		color = vec4(0.0, 0.0, 0.0, 1.0);\n"
				<< "	}\n";

	return new glu::ShaderProgram(m_context.getRenderContext(), glu::ProgramSources()
					<< glu::VertexSource(generateVertexShader(m_shaderType, shaderUniformDecl.str(), shaderBody.str()))
					<< glu::FragmentSource(generateFragmentShader(m_shaderType, shaderUniformDecl.str(), shaderBody.str())));
}


class SSBOBindingNegativeCase : public LayoutBindingNegativeCase
{
public:
											SSBOBindingNegativeCase			(Context&		context,
																			 const char*	name,
																			 const char*	desc,
																			 ShaderType		shaderType,
																			 TestType		testType,
																			 ErrorType		errorType);
											~SSBOBindingNegativeCase		(void);

private:
	glu::ShaderProgram*						generateShaders					(void) const;
};

SSBOBindingNegativeCase::SSBOBindingNegativeCase (Context& context,
												  const char* name,
												  const char* desc,
												  ShaderType shaderType,
												  TestType testType,
												  ErrorType errorType)
	: LayoutBindingNegativeCase(context,
								name,
								desc,
								shaderType,
								testType,
								errorType,
								GL_MAX_SHADER_STORAGE_BUFFER_BINDINGS,
								GL_MAX_VERTEX_SHADER_STORAGE_BLOCKS,
								GL_MAX_TESS_CONTROL_SHADER_STORAGE_BLOCKS,
								GL_MAX_TESS_EVALUATION_SHADER_STORAGE_BLOCKS,
								GL_MAX_FRAGMENT_SHADER_STORAGE_BLOCKS,
								GL_MAX_COMBINED_SHADER_STORAGE_BLOCKS,
								"ColorBuffer")
{
}

SSBOBindingNegativeCase::~SSBOBindingNegativeCase (void)
{
	deinit();
}

glu::ShaderProgram* SSBOBindingNegativeCase::generateShaders (void) const
{
	std::ostringstream		vertexUniformDecl;
	std::ostringstream		fragmentUniformDecl;
	std::ostringstream		tessCtrlUniformDecl;
	std::ostringstream		tessEvalUniformDecl;
	std::ostringstream		shaderBody;
	const bool				arrayInstance	= (m_testType == TESTTYPE_BINDING_ARRAY || m_testType == TESTTYPE_BINDING_MAX_ARRAY);
	const int				numDeclarations = (arrayInstance ? 1 : m_numBindings);

	// Generate the uniform declarations for the vertex and fragment shaders
	for (int declNdx = 0; declNdx < numDeclarations; ++declNdx)
	{
		vertexUniformDecl << "layout(std140, binding = " << m_vertexShaderBinding[declNdx] << ") buffer "
			<< getUniformName(m_uniformName, declNdx) << "\n"
			<< "{\n"
			<< "	highp vec4 color1;\n"
			<< "	highp vec4 color2;\n"
			<< "} " << (arrayInstance ? getUniformName("colors", declNdx, m_numBindings) : getUniformName("colors", declNdx)) << ";\n";

		fragmentUniformDecl << "layout(std140, binding = " << m_fragmentShaderBinding[declNdx] << ") buffer "
			<< getUniformName(m_uniformName, declNdx) << "\n"
			<< "{\n"
			<< "	highp vec4 color1;\n"
			<< "	highp vec4 color2;\n"
			<< "} " << (arrayInstance ? getUniformName("colors", declNdx, m_numBindings) : getUniformName("colors", declNdx)) << ";\n";

		tessCtrlUniformDecl << "layout(std140, binding = " << m_tessCtrlShaderBinding[declNdx] << ") buffer "
			<< getUniformName(m_uniformName, declNdx) << "\n"
			<< "{\n"
			<< "	highp vec4 color1;\n"
			<< "	highp vec4 color2;\n"
			<< "} " << (arrayInstance ? getUniformName("colors", declNdx, m_numBindings) : getUniformName("colors", declNdx)) << ";\n";

		tessEvalUniformDecl << "layout(std140, binding = " << m_tessEvalShaderBinding[declNdx] << ") buffer "
			<< getUniformName(m_uniformName, declNdx) << "\n"
			<< "{\n"
			<< "	highp vec4 color1;\n"
			<< "	highp vec4 color2;\n"
			<< "} " << (arrayInstance ? getUniformName("colors", declNdx, m_numBindings) : getUniformName("colors", declNdx)) << ";\n";
	}

	// Generate the shader body for the vertex and fragment shaders
	for (int bindNdx = 0; bindNdx < m_numBindings*2; ++bindNdx)	// Multiply by two to cover cases for both colors for each UBO
	{
		const std::string uname = (arrayInstance ? getUniformName("colors", 0, bindNdx/2) : getUniformName("colors", bindNdx/2));
		shaderBody	<< "	" << (bindNdx == 0 ? "if" : "else if") << " (u_arrayNdx == " << de::toString(bindNdx) << ")\n"
					<< "	{\n"
					<< "		color = " << uname << (bindNdx%2 == 0 ? ".color1" : ".color2") << ";\n"
					<< "	}\n";
	}

	shaderBody	<< "	else\n"
				<< "	{\n"
				<< "		color = vec4(0.0, 0.0, 0.0, 1.0);\n"
				<< "	}\n";

	glu::ProgramSources sources = glu::ProgramSources()
				<< glu::VertexSource(generateVertexShader(m_shaderType, vertexUniformDecl.str(), shaderBody.str()))
				<< glu::FragmentSource(generateFragmentShader(m_shaderType, fragmentUniformDecl.str(), shaderBody.str()));

	if (m_tessSupport)
		sources << glu::TessellationControlSource(generateTessControlShader(m_shaderType, tessCtrlUniformDecl.str(), shaderBody.str()))
				<< glu::TessellationEvaluationSource(generateTessEvaluationShader(m_shaderType, tessEvalUniformDecl.str(), shaderBody.str()));

	return new glu::ShaderProgram(m_context.getRenderContext(), sources);
}


} // Anonymous

LayoutBindingTests::LayoutBindingTests (Context& context)
	: TestCaseGroup (context, "layout_binding", "Layout binding tests")
{
}

LayoutBindingTests::~LayoutBindingTests (void)
{
}

void LayoutBindingTests::init (void)
{
	// Render test groups
	tcu::TestCaseGroup* const samplerBindingTestGroup			= new tcu::TestCaseGroup(m_testCtx, "sampler",		"Test sampler layout binding");
	tcu::TestCaseGroup* const sampler2dBindingTestGroup			= new tcu::TestCaseGroup(m_testCtx, "sampler2d",	"Test sampler2d layout binding");
	tcu::TestCaseGroup* const sampler3dBindingTestGroup			= new tcu::TestCaseGroup(m_testCtx, "sampler3d",	"Test sampler3d layout binding");

	tcu::TestCaseGroup* const imageBindingTestGroup				= new tcu::TestCaseGroup(m_testCtx, "image",		"Test image layout binding");
	tcu::TestCaseGroup* const image2dBindingTestGroup			= new tcu::TestCaseGroup(m_testCtx, "image2d",		"Test image2d layout binding");
	tcu::TestCaseGroup* const image3dBindingTestGroup			= new tcu::TestCaseGroup(m_testCtx, "image3d",		"Test image3d layout binding");

	tcu::TestCaseGroup* const UBOBindingTestGroup				= new tcu::TestCaseGroup(m_testCtx, "ubo",			"Test UBO layout binding");
	tcu::TestCaseGroup* const SSBOBindingTestGroup				= new tcu::TestCaseGroup(m_testCtx, "ssbo",			"Test SSBO layout binding");

	// Negative test groups
	tcu::TestCaseGroup* const negativeBindingTestGroup			= new tcu::TestCaseGroup(m_testCtx, "negative",		"Test layout binding with invalid bindings");

	tcu::TestCaseGroup* const negativeSamplerBindingTestGroup	= new tcu::TestCaseGroup(m_testCtx, "sampler",		"Test sampler layout binding with invalid bindings");
	tcu::TestCaseGroup* const negativeSampler2dBindingTestGroup	= new tcu::TestCaseGroup(m_testCtx, "sampler2d",	"Test sampler2d layout binding with invalid bindings");
	tcu::TestCaseGroup* const negativeSampler3dBindingTestGroup	= new tcu::TestCaseGroup(m_testCtx, "sampler3d",	"Test sampler3d layout binding with invalid bindings");

	tcu::TestCaseGroup* const negativeImageBindingTestGroup		= new tcu::TestCaseGroup(m_testCtx, "image",		"Test image layout binding with invalid bindings");
	tcu::TestCaseGroup* const negativeImage2dBindingTestGroup	= new tcu::TestCaseGroup(m_testCtx, "image2d",		"Test image2d layout binding with invalid bindings");
	tcu::TestCaseGroup* const negativeImage3dBindingTestGroup	= new tcu::TestCaseGroup(m_testCtx, "image3d",		"Test image3d layout binding with invalid bindings");

	tcu::TestCaseGroup* const negativeUBOBindingTestGroup		= new tcu::TestCaseGroup(m_testCtx, "ubo",			"Test UBO layout binding with invalid bindings");
	tcu::TestCaseGroup* const negativeSSBOBindingTestGroup		= new tcu::TestCaseGroup(m_testCtx, "ssbo",			"Test SSBO layout binding with invalid bindings");

	static const struct RenderTestType
	{
		ShaderType				shaderType;
		TestType				testType;
		std::string				name;
		std::string				descPostfix;
	} s_renderTestTypes[] =
	{
		{ SHADERTYPE_VERTEX,	TESTTYPE_BINDING_SINGLE,		"vertex_binding_single",		"a single instance" },
		{ SHADERTYPE_VERTEX,	TESTTYPE_BINDING_MAX,			"vertex_binding_max",			"maximum binding point"	},
		{ SHADERTYPE_VERTEX,	TESTTYPE_BINDING_MULTIPLE,		"vertex_binding_multiple",		"multiple instances"},
		{ SHADERTYPE_VERTEX,	TESTTYPE_BINDING_ARRAY,			"vertex_binding_array",			"an array instance" },
		{ SHADERTYPE_VERTEX,	TESTTYPE_BINDING_MAX_ARRAY,		"vertex_binding_max_array",		"an array instance with maximum binding point" },

		{ SHADERTYPE_FRAGMENT,	TESTTYPE_BINDING_SINGLE,		"fragment_binding_single",		"a single instance" },
		{ SHADERTYPE_FRAGMENT,	TESTTYPE_BINDING_MAX,			"fragment_binding_max",			"maximum binding point"	},
		{ SHADERTYPE_FRAGMENT,	TESTTYPE_BINDING_MULTIPLE,		"fragment_binding_multiple",	"multiple instances"},
		{ SHADERTYPE_FRAGMENT,	TESTTYPE_BINDING_ARRAY,			"fragment_binding_array",		"an array instance" },
		{ SHADERTYPE_FRAGMENT,	TESTTYPE_BINDING_MAX_ARRAY,		"fragment_binding_max_array",	"an array instance with maximum binding point" },
	};

	static const struct NegativeTestType
	{
		ShaderType								shaderType;
		TestType								testType;
		LayoutBindingNegativeCase::ErrorType	errorType;
		std::string								name;
		std::string								descPostfix;
	} s_negativeTestTypes[] =
	{
		{ SHADERTYPE_VERTEX,			TESTTYPE_BINDING_SINGLE,		LayoutBindingNegativeCase::ERRORTYPE_OVER_MAX_UNITS,	"vertex_binding_over_max",					"over maximum binding point"   },
		{ SHADERTYPE_FRAGMENT,			TESTTYPE_BINDING_SINGLE,		LayoutBindingNegativeCase::ERRORTYPE_OVER_MAX_UNITS,	"fragment_binding_over_max",				"over maximum binding point"   },
		{ SHADERTYPE_TESS_CONTROL,		TESTTYPE_BINDING_SINGLE,		LayoutBindingNegativeCase::ERRORTYPE_OVER_MAX_UNITS,	"tess_control_binding_over_max",			"over maximum binding point"   },
		{ SHADERTYPE_TESS_EVALUATION,	TESTTYPE_BINDING_SINGLE,		LayoutBindingNegativeCase::ERRORTYPE_OVER_MAX_UNITS,	"tess_evaluation_binding_over_max",			"over maximum binding point"   },
		{ SHADERTYPE_VERTEX,			TESTTYPE_BINDING_SINGLE,		LayoutBindingNegativeCase::ERRORTYPE_LESS_THAN_ZERO,	"vertex_binding_neg",						"negative binding point"	   },
		{ SHADERTYPE_FRAGMENT,			TESTTYPE_BINDING_SINGLE,		LayoutBindingNegativeCase::ERRORTYPE_LESS_THAN_ZERO,	"fragment_binding_neg",						"negative binding point"	   },
		{ SHADERTYPE_TESS_CONTROL,		TESTTYPE_BINDING_SINGLE,		LayoutBindingNegativeCase::ERRORTYPE_LESS_THAN_ZERO,	"tess_control_binding_neg",					"negative binding point"	   },
		{ SHADERTYPE_TESS_EVALUATION,	TESTTYPE_BINDING_SINGLE,		LayoutBindingNegativeCase::ERRORTYPE_LESS_THAN_ZERO,	"tess_evaluation_binding_neg",				"negative binding point"	   },

		{ SHADERTYPE_VERTEX,			TESTTYPE_BINDING_ARRAY,			LayoutBindingNegativeCase::ERRORTYPE_OVER_MAX_UNITS,	"vertex_binding_over_max_array",			"over maximum binding point"   },
		{ SHADERTYPE_FRAGMENT,			TESTTYPE_BINDING_ARRAY,			LayoutBindingNegativeCase::ERRORTYPE_OVER_MAX_UNITS,	"fragment_binding_over_max_array",			"over maximum binding point"   },
		{ SHADERTYPE_TESS_CONTROL,		TESTTYPE_BINDING_ARRAY,			LayoutBindingNegativeCase::ERRORTYPE_OVER_MAX_UNITS,	"tess_control_binding_over_max_array",		"over maximum binding point"   },
		{ SHADERTYPE_TESS_EVALUATION,	TESTTYPE_BINDING_ARRAY,			LayoutBindingNegativeCase::ERRORTYPE_OVER_MAX_UNITS,	"tess_evaluation_binding_over_max_array",	"over maximum binding point"   },
		{ SHADERTYPE_VERTEX,			TESTTYPE_BINDING_ARRAY,			LayoutBindingNegativeCase::ERRORTYPE_LESS_THAN_ZERO,	"vertex_binding_neg_array",					"negative binding point"	   },
		{ SHADERTYPE_FRAGMENT,			TESTTYPE_BINDING_ARRAY,			LayoutBindingNegativeCase::ERRORTYPE_LESS_THAN_ZERO,	"fragment_binding_neg_array",				"negative binding point"	   },
		{ SHADERTYPE_TESS_CONTROL,		TESTTYPE_BINDING_ARRAY,			LayoutBindingNegativeCase::ERRORTYPE_LESS_THAN_ZERO,	"tess_control_binding_neg_array",			"negative binding point"	   },
		{ SHADERTYPE_TESS_EVALUATION,	TESTTYPE_BINDING_ARRAY,			LayoutBindingNegativeCase::ERRORTYPE_LESS_THAN_ZERO,	"tess_evaluation_binding_neg_array",		"negative binding point"	   },

		{ SHADERTYPE_ALL,				TESTTYPE_BINDING_SINGLE,		LayoutBindingNegativeCase::ERRORTYPE_CONTRADICTORY,		"binding_contradictory",					"contradictory binding points" },
		{ SHADERTYPE_ALL,				TESTTYPE_BINDING_ARRAY,			LayoutBindingNegativeCase::ERRORTYPE_CONTRADICTORY,		"binding_contradictory_array",				"contradictory binding points" },
	};

	// Render tests
	for (int testNdx = 0; testNdx < DE_LENGTH_OF_ARRAY(s_renderTestTypes); ++testNdx)
	{
		const RenderTestType& test = s_renderTestTypes[testNdx];

		// Render sampler binding tests
		sampler2dBindingTestGroup->addChild(new SamplerBindingRenderCase(m_context, test.name.c_str(), ("Sampler2D layout binding with " + test.descPostfix).c_str(), test.shaderType, test.testType, GL_SAMPLER_2D, GL_TEXTURE_2D));
		sampler3dBindingTestGroup->addChild(new SamplerBindingRenderCase(m_context, test.name.c_str(), ("Sampler3D layout binding with " + test.descPostfix).c_str(), test.shaderType, test.testType, GL_SAMPLER_3D, GL_TEXTURE_3D));

		// Render image binding tests
		image2dBindingTestGroup->addChild(new ImageBindingRenderCase(m_context, test.name.c_str(), ("Image2D layout binding with " + test.descPostfix).c_str(), test.shaderType, test.testType, GL_IMAGE_2D, GL_TEXTURE_2D));
		image3dBindingTestGroup->addChild(new ImageBindingRenderCase(m_context, test.name.c_str(), ("Image3D layout binding with " + test.descPostfix).c_str(), test.shaderType, test.testType, GL_IMAGE_3D, GL_TEXTURE_3D));

		// Render UBO binding tests
		UBOBindingTestGroup->addChild(new UBOBindingRenderCase(m_context, test.name.c_str(), ("UBO layout binding with " + test.descPostfix).c_str(), test.shaderType, test.testType));

		// Render SSBO binding tests
		SSBOBindingTestGroup->addChild(new SSBOBindingRenderCase(m_context, test.name.c_str(), ("SSBO layout binding with " + test.descPostfix).c_str(), test.shaderType, test.testType));
	}

	// Negative binding tests
	for (int testNdx = 0; testNdx < DE_LENGTH_OF_ARRAY(s_negativeTestTypes); ++testNdx)
	{
		const NegativeTestType& test = s_negativeTestTypes[testNdx];

		// Negative sampler binding tests
		negativeSampler2dBindingTestGroup->addChild(new SamplerBindingNegativeCase(m_context, test.name.c_str(), ("Invalid sampler2d layout binding using " + test.descPostfix).c_str(), test.shaderType, test.testType, test.errorType, GL_SAMPLER_2D));
		negativeSampler3dBindingTestGroup->addChild(new SamplerBindingNegativeCase(m_context, test.name.c_str(), ("Invalid sampler3d layout binding using " + test.descPostfix).c_str(), test.shaderType, test.testType, test.errorType, GL_SAMPLER_3D));

		// Negative image binding tests
		negativeImage2dBindingTestGroup->addChild(new ImageBindingNegativeCase(m_context, test.name.c_str(), ("Invalid image2d layout binding using " + test.descPostfix).c_str(), test.shaderType, test.testType, test.errorType, GL_IMAGE_2D));
		negativeImage3dBindingTestGroup->addChild(new ImageBindingNegativeCase(m_context, test.name.c_str(), ("Invalid image3d layout binding using " + test.descPostfix).c_str(), test.shaderType, test.testType, test.errorType, GL_IMAGE_3D));

		// Negative UBO binding tests
		negativeUBOBindingTestGroup->addChild(new UBOBindingNegativeCase(m_context, test.name.c_str(), ("Invalid UBO layout binding using " + test.descPostfix).c_str(), test.shaderType, test.testType, test.errorType));

		// Negative SSBO binding tests
		negativeSSBOBindingTestGroup->addChild(new SSBOBindingNegativeCase(m_context, test.name.c_str(), ("Invalid SSBO layout binding using " + test.descPostfix).c_str(), test.shaderType, test.testType, test.errorType));
	}

	samplerBindingTestGroup->addChild(sampler2dBindingTestGroup);
	samplerBindingTestGroup->addChild(sampler3dBindingTestGroup);

	imageBindingTestGroup->addChild(image2dBindingTestGroup);
	imageBindingTestGroup->addChild(image3dBindingTestGroup);

	negativeSamplerBindingTestGroup->addChild(negativeSampler2dBindingTestGroup);
	negativeSamplerBindingTestGroup->addChild(negativeSampler3dBindingTestGroup);

	negativeImageBindingTestGroup->addChild(negativeImage2dBindingTestGroup);
	negativeImageBindingTestGroup->addChild(negativeImage3dBindingTestGroup);

	negativeBindingTestGroup->addChild(negativeSamplerBindingTestGroup);
	negativeBindingTestGroup->addChild(negativeUBOBindingTestGroup);
	negativeBindingTestGroup->addChild(negativeSSBOBindingTestGroup);
	negativeBindingTestGroup->addChild(negativeImageBindingTestGroup);

	addChild(samplerBindingTestGroup);
	addChild(UBOBindingTestGroup);
	addChild(SSBOBindingTestGroup);
	addChild(imageBindingTestGroup);
	addChild(negativeBindingTestGroup);
}

} // Functional
} // gles31
} // deqp
